Check if a class has a member function of a given signature
我要求使用模板技巧来检测类是否具有给定签名的特定成员函数。
这个问题与这里引用的问题类似网址:http://www.gotw.ca/gotw/071.htm但并非如此:在萨特的书中,他回答了一个问题,即C类必须提供一个具有特定签名的成员函数,否则程序将无法编译。在我的问题中,如果一个类有这个函数,我需要做一些事情,否则就做"其他事情"。
Boost::Serialization也遇到了类似的问题,但我不喜欢他们采用的解决方案:一个模板函数,它默认调用一个带有特定签名的自由函数(必须定义),除非您定义了一个带有特定签名的特定成员函数(在这种情况下,"Serialization"接受两个给定类型的参数)。否则会发生编译错误。即实现侵入式和非侵入式序列化。
我不喜欢这个解决方案有两个原因:
我需要为没有该成员函数的类定义自定义行为,并且我的实体在不同的命名空间中(我不想在另一个命名空间中重写在一个命名空间中定义的全局函数)
你能给我一个提示来解决这个难题吗?
型
这是一个依赖C++ 11特性的可能实现。它可以正确地检测函数,即使它是继承的(不像迈克·金汉在他的答案中观察到的,接受的答案中的解决方案)。
此代码段测试的函数称为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include <type_traits> // Primary template with a static assertion // for a meaningful error message // if it ever gets instantiated. // We could leave it undefined if we didn't care. template<typename, typename T> struct has_serialize { static_assert( std::integral_constant<T, false>::value, "Second template parameter needs to be of function type."); }; // specialization that does the checking template<typename C, typename Ret, typename... Args> struct has_serialize<C, Ret(Args...)> { private: template<typename T> static constexpr auto check(T*) -> typename std::is_same< decltype( std::declval<T>().serialize( std::declval<Args>()... ) ), Ret // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >::type; // attempt to call it and see if the return type is correct template<typename> static constexpr std::false_type check(...); typedef decltype(check<C>(0)) type; public: static constexpr bool value = type::value; }; |
。
用法:
1 2 3 4 5 6 7 | struct X { int serialize(const std::string&) { return 42; } }; struct Y : X {}; std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1 |
型
我不确定我是否正确理解您,但是您可以利用sfinae在编译时检测函数是否存在。代码中的示例(测试类是否具有成员函数大小u t used u memory()const)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | template<typename T> struct HasUsedMemoryMethod { template<typename U, size_t (U::*)() const> struct SFINAE {}; template<typename U> static char Test(SFINAE<U, &U::used_memory>*); template<typename U> static int Test(...); static const bool Has = sizeof(Test<T>(0)) == sizeof(char); }; template<typename TMap> void ReportMemUsage(const TMap& m, std::true_type) { // We may call used_memory() on m here. } template<typename TMap> void ReportMemUsage(const TMap&, std::false_type) { } template<typename TMap> void ReportMemUsage(const TMap& m) { ReportMemUsage(m, std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>()); } |
对compileTime成员函数这个问题的公认答案反省虽然很受欢迎,但有一个可以观察到的障碍在以下程序中:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <type_traits> #include <iostream> #include <memory> /* Here we apply the accepted answer's technique to probe for the the existence of `E T::operator*() const` */ template<typename T, typename E> struct has_const_reference_op { template<typename U, E (U::*)() const> struct SFINAE {}; template<typename U> static char Test(SFINAE<U, &U::operator*>*); template<typename U> static int Test(...); static const bool value = sizeof(Test<T>(0)) == sizeof(char); }; using namespace std; /* Here we test the `std::` smart pointer templates, including the deprecated `auto_ptr<T>`, to determine in each case whether T = (the template instantiated for `int`) provides `int & T::operator*() const` - which all of them in fact do. */ int main(void) { cout << has_const_reference_op,int &>::value; cout << has_const_reference_op<unique_ptr<int>,int &>::value; cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl; return 0; } |
用GCC4.6.3构建,程序输出
如果你还不明白这一点,那就看看收割台中的
这个障碍不影响著名的sfinae方法,使用"sizeof()技巧",仅用于检测
一种用于
为了回答这个约束条件下的问题,我将说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include <type_traits> /*! The template `has_const_reference_op<T,E>` exports a boolean constant `value that is true iff `T` provides `E T::operator*() const` */ template< typename T, typename E> struct has_const_reference_op { /* SFINAE operator-has-correct-sig :) */ template<typename A> static std::true_type test(E (A::*)() const) { return std::true_type(); } /* SFINAE operator-exists :) */ template <typename A> static decltype(test(&A::operator*)) test(decltype(&A::operator*),void *) { /* Operator exists. What about sig? */ typedef decltype(test(&A::operator*)) return_type; return return_type(); } /* SFINAE game over :( */ template<typename A> static std::false_type test(...) { return std::false_type(); } /* This will be either `std::true_type` or `std::false_type` */ typedef decltype(test<T>(0,0)) type; static const bool value = type::value; /* Which is it? */ }; |
在这个解决方案中,重载的sfinae探测函数
我们需要调查至少一个和最多两个信息点:好的。
T::operator*() 是否存在?如果没有,我们就完了。- 鉴于
T::operator*() 存在,其签名是EDOCX1?18?
我们通过评估单个呼叫的返回类型得到答案。至
1 | typedef decltype(test<T>(0,0)) type; |
此调用可能会解决
我们为什么要过两个?只是为了迫使决议排除
对
让我们假设编译器对此表示同意。那它就跟
这次,我们只传递一个论点,
如果
如果编译器没有为调用
下面是一个测试程序,显示生成预期不同案例样本的答案(GCC第4.6.3条)。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | // To test struct empty{}; // To test struct int_ref { int & operator*() const { return *_pint; } int & foo() const { return *_pint; } int * _pint; }; // To test struct sub_int_ref : int_ref{}; // To test template<typename E> struct ee_ref { E & operator*() { return *_pe; } E & foo() const { return *_pe; } E * _pe; }; // To test struct sub_ee_ref : ee_ref<char>{}; using namespace std; #include <iostream> #include <memory> #include <vector> int main(void) { cout <<"Expect Yes" << endl; cout << has_const_reference_op,int &>::value; cout << has_const_reference_op<unique_ptr<int>,int &>::value; cout << has_const_reference_op<shared_ptr<int>,int &>::value; cout << has_const_reference_op<std::vector<int>::iterator,int &>::value; cout << has_const_reference_op<std::vector<int>::const_iterator, int const &>::value; cout << has_const_reference_op<int_ref,int &>::value; cout << has_const_reference_op<sub_int_ref,int &>::value << endl; cout <<"Expect No" << endl; cout << has_const_reference_op<int *,int &>::value; cout << has_const_reference_op<unique_ptr<int>,char &>::value; cout << has_const_reference_op<unique_ptr<int>,int const &>::value; cout << has_const_reference_op<unique_ptr<int>,int>::value; cout << has_const_reference_op<unique_ptr<long>,int &>::value; cout << has_const_reference_op<int,int>::value; cout << has_const_reference_op<std::vector<int>,int &>::value; cout << has_const_reference_op<ee_ref<int>,int &>::value; cout << has_const_reference_op<sub_ee_ref,int &>::value; cout << has_const_reference_op::value << endl; return 0; } |
这个想法有新的缺陷吗?它能再普通一次吗与它避免的障碍物相撞?好的。好啊。
以下是一些用法片段:*所有这些的胆量都在进一步下降。
检查给定类中的成员
1 2 | CREATE_MEMBER_CHECK(x); bool has_x = has_member_x<class_to_check_for_x>::value; |
检查成员函数
1 2 3 | //Func signature MUST have T as template variable here... simpler this way :\ CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value; |
检查成员变量
1 2 | CREATE_MEMBER_VAR_CHECK(x); bool has_var_x = has_member_var_x<class_to_check_for_x>::value; |
检查成员类
1 2 | CREATE_MEMBER_CLASS_CHECK(x); bool has_class_x = has_member_class_x<class_to_check_for_x>::value; |
检查是否有会员工会
1 2 | CREATE_MEMBER_UNION_CHECK(x); bool has_union_x = has_member_union_x<class_to_check_for_x>::value; |
检查成员枚举
1 2 | CREATE_MEMBER_ENUM_CHECK(x); bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value; |
检查是否有任何成员函数
1 2 3 4 5 6 7 | CREATE_MEMBER_CHECK(x); CREATE_MEMBER_VAR_CHECK(x); CREATE_MEMBER_CLASS_CHECK(x); CREATE_MEMBER_UNION_CHECK(x); CREATE_MEMBER_ENUM_CHECK(x); CREATE_MEMBER_FUNC_CHECK(x); bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; |
或
1 2 | CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value; |
细节和核心:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /* - Multiple inheritance forces ambiguity of member names. - SFINAE is used to make aliases to member names. - Expression SFINAE is used in just one generic has_member that can accept any alias we pass it. */ //Variadic to force ambiguity of class members. C++11 and up. template <typename... Args> struct ambiguate : public Args... {}; //Non-variadic version of the line above. //template <typename A, typename B> struct ambiguate : public A, public B {}; template<typename A, typename = void> struct got_type : std::false_type {}; template<typename A> struct got_type<A> : std::true_type { typedef A type; }; template<typename T, T> struct sig_check : std::true_type {}; template<typename Alias, typename AmbiguitySeed> struct has_member { template<typename C> static char ((&f(decltype(&C::value))))[1]; template<typename C> static char ((&f(...)))[2]; //Make sure the member name is consistently spelled the same. static_assert( (sizeof(f<AmbiguitySeed>(0)) == 1) ,"Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." ); static bool const value = sizeof(f<Alias>(0)) == 2; }; |
宏(el diablo!):
创建成员检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Check for any member with given name, whether var, func, class, union, enum. #define CREATE_MEMBER_CHECK(member) \ \ template<typename T, typename = std::true_type> \ struct Alias_##member; \ \ template<typename T> \ struct Alias_##member < \ T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \ > { static const decltype(&T::member) value; }; \ \ struct AmbiguitySeed_##member { char member; }; \ \ template<typename T> \ struct has_member_##member { \ static const bool value \ = has_member< \ Alias_##member> \ , Alias_##member<AmbiguitySeed_##member> \ >::value \ ; \ } |
创建成员变量检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //Check for member variable with given name. #define CREATE_MEMBER_VAR_CHECK(var_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_var_##var_name : std::false_type {}; \ \ template<typename T> \ struct has_member_var_##var_name< \ T \ , std::integral_constant< \ bool \ , !std::is_member_function_pointer<decltype(&T::var_name)>::value \ > \ > : std::true_type {} |
创建成员检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 | //Check for member function with given name AND signature. #define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ \ template<typename T, typename = std::true_type> \ struct has_member_func_##templ_postfix : std::false_type {}; \ \ template<typename T> \ struct has_member_func_##templ_postfix< \ T, std::integral_constant< \ bool \ , sig_check<func_sig, &T::func_name>::value \ > \ > : std::true_type {} |
创建u成员u类u检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Check for member class with given name. #define CREATE_MEMBER_CLASS_CHECK(class_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_class_##class_name : std::false_type {}; \ \ template<typename T> \ struct has_member_class_##class_name< \ T \ , std::integral_constant< \ bool \ , std::is_class< \ typename got_type<typename T::class_name>::type \ >::value \ > \ > : std::true_type {} |
创建成员联合检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Check for member union with given name. #define CREATE_MEMBER_UNION_CHECK(union_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_union_##union_name : std::false_type {}; \ \ template<typename T> \ struct has_member_union_##union_name< \ T \ , std::integral_constant< \ bool \ , std::is_union< \ typename got_type<typename T::union_name>::type \ >::value \ > \ > : std::true_type {} |
创建成员枚举检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Check for member enum with given name. #define CREATE_MEMBER_ENUM_CHECK(enum_name) \ \ template<typename T, typename = std::true_type> \ struct has_member_enum_##enum_name : std::false_type {}; \ \ template<typename T> \ struct has_member_enum_##enum_name< \ T \ , std::integral_constant< \ bool \ , std::is_enum< \ typename got_type<typename T::enum_name>::type \ >::value \ > \ > : std::true_type {} |
创建成员检查:
1 2 3 4 5 6 7 8 9 10 11 12 | //Check for function with given name, any signature. #define CREATE_MEMBER_FUNC_CHECK(func) \ template<typename T> \ struct has_member_func_##func { \ static const bool value \ = has_member_##func<T>::value \ && !has_member_var_##func<T>::value \ && !has_member_class_##func<T>::value \ && !has_member_union_##func<T>::value \ && !has_member_enum_##func<T>::value \ ; \ } |
创建成员检查:
1 2 3 4 5 6 7 8 | //Create all the checks for one member. Does NOT include func sig checks. #define CREATE_MEMBER_CHECKS(member) \ CREATE_MEMBER_CHECK(member); \ CREATE_MEMBER_VAR_CHECK(member); \ CREATE_MEMBER_CLASS_CHECK(member); \ CREATE_MEMBER_UNION_CHECK(member); \ CREATE_MEMBER_ENUM_CHECK(member); \ CREATE_MEMBER_FUNC_CHECK(member) |
型
如果您知道所期望的成员函数的名称,这就足够了。(在这种情况下,如果没有成员函数,那么函数bla将无法实例化(编写一个无论如何都有效的函数是很困难的,因为缺少函数部分专门化)。您可能还需要使用类模板)另外,启用结构(类似于启用_if)也可以在您希望它作为成员的函数类型上进行模板化。
1 2 3 4 5 6 7 8 9 10 11 | template <typename T, int (T::*) ()> struct enable { typedef T type; }; template <typename T> typename enable<T, &T::i>::type bla (T&); struct A { void i(); }; struct B { int i(); }; int main() { A a; B b; bla(b); bla(a); } |
号
下面是对迈克·金汉的回答的简单理解。这将检测继承的方法。它还将检查准确的签名(不像jrok允许参数转换的方法)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | template <class C> class HasGreetMethod { template <class T> static std::true_type testSignature(void (T::*)(const char*) const); template <class T> static decltype(testSignature(&T::greet)) test(std::nullptr_t); template <class T> static std::false_type test(...); public: using type = decltype(test<C>(nullptr)); static const bool value = type::value; }; struct A { void greet(const char* name) const; }; struct Derived : A { }; static_assert(HasGreetMethod<Derived>::value,""); |
可运行示例
要实现这一点,我们需要使用:
你可以在这里看到一个活生生的例子。但我也会在下面解释:
我想检查是否存在一个名为
1 2 | template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int); template <typename T> static false_type hasTest(...); |
decltype(hasTest(0))::value 为true (注:不需要创建特殊功能来处理void a::test() 过载,接受void a::test(int) )decltype(hasTest(0))::value 为true (因为int 可转换为double int b::test(double) 可接受,与返回类型无关)decltype(hasTest 是(0))::value false (c 没有名为test 的方法接受可从int 转换的类型,因此不接受)
此解决方案有两个缺点:
因此,这些函数必须在一个详细的名称空间中声明,或者理想情况下,如果它们只与一个类一起使用,那么它们应该由该类私下声明。为此,我编写了一个宏来帮助您抽象这些信息:
1 2 3 | #define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \ template <typename T> static false_type __ ## DEFINE(...); \ template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0)); |
您可以这样使用:
1 2 3 4 | namespace details { FOO(test(declval<int>()), test_int) FOO(test(), test_void) } |
随后调用
型
我自己也有同样的问题,发现这里提出的解决方案很有趣…但需要一个解决方案:
百万千克1检测继承的函数;百万千克1百万千克1与非C++ 11就绪编译器兼容(所以没有解密)百万千克1
基于一个提振讨论,找到了另一个类似这样的建议。下面是根据boost::has_ux类的模型,将建议的解决方案概括为两个traits类的宏声明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <boost/type_traits/is_class.hpp> #include <boost/mpl/vector.hpp> /// Has constant function /** \param func_ret_type Function return type \param func_name Function name \param ... Variadic arguments are for the function parameters */ #define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \ __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__) /// Has non-const function /** \param func_ret_type Function return type \param func_name Function name \param ... Variadic arguments are for the function parameters */ #define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \ __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__) // Traits content #define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...) \ template \ < typename Type, \ bool is_class = boost::is_class<Type>::value \ > \ class has_func_ ## func_name; \ template<typename Type> \ class has_func_ ## func_name<Type,false> \ {public: \ BOOST_STATIC_CONSTANT( bool, value = false ); \ typedef boost::false_type type; \ }; \ template<typename Type> \ class has_func_ ## func_name<Type,true> \ { struct yes { char _foo; }; \ struct no { yes _foo[2]; }; \ struct Fallback \ { func_ret_type func_name( __VA_ARGS__ ) \ UTILITY_OPTIONAL(func_const,const) {} \ }; \ struct Derived : public Type, public Fallback {}; \ template <typename T, T t> class Helper{}; \ template <typename U> \ static no deduce(U*, Helper \ < func_ret_type (Fallback::*)( __VA_ARGS__ ) \ UTILITY_OPTIONAL(func_const,const), \ &U::func_name \ >* = 0 \ ); \ static yes deduce(...); \ public: \ BOOST_STATIC_CONSTANT( \ bool, \ value = sizeof(yes) \ == sizeof( deduce( static_cast<Derived*>(0) ) ) \ ); \ typedef ::boost::integral_constant<bool,value> type; \ BOOST_STATIC_CONSTANT(bool, is_const = func_const); \ typedef func_ret_type return_type; \ typedef ::boost::mpl::vector< __VA_ARGS__ > args_type; \ } // Utility functions #define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ ) #define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ ) #define __UTILITY_OPTIONAL_0(...) #define __UTILITY_OPTIONAL_1(...) __VA_ARGS__ |
。
这些宏扩展到具有以下原型的traits类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | template<class T> class has_func_[func_name] { public: /// Function definition result value /** Tells if the tested function is defined for type T or not. */ static const bool value = true | false; /// Function definition result type /** Type representing the value attribute usable in http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html */ typedef boost::integral_constant<bool,value> type; /// Tested function constness indicator /** Indicates if the tested function is const or not. This value is not deduced, it is forced depending on the user call to one of the traits generators. */ static const bool is_const = true | false; /// Tested function return type /** Indicates the return type of the tested function. This value is not deduced, it is forced depending on the user's arguments to the traits generators. */ typedef func_ret_type return_type; /// Tested function arguments types /** Indicates the arguments types of the tested function. This value is not deduced, it is forced depending on the user's arguments to the traits generators. */ typedef ::boost::mpl::vector< __VA_ARGS__ > args_type; }; |
那么,我们能用它做什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // We enclose the traits class into // a namespace to avoid collisions namespace ns_0 { // Next line will declare the traits class // to detect the member function void foo(int,int) const DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int); } // we can use BOOST to help in using the traits #include <boost/utility/enable_if.hpp> // Here is a function that is active for types // declaring the good member function template<typename T> inline typename boost::enable_if< ns_0::has_func_foo<T> >::type foo_bar(const T &_this_, int a=0, int b=1) { _this_.foo(a,b); } // Here is a function that is active for types // NOT declaring the good member function template<typename T> inline typename boost::disable_if< ns_0::has_func_foo<T> >::type foo_bar(const T &_this_, int a=0, int b=1) { default_foo(_this_,a,b); } // Let us declare test types struct empty { }; struct direct_foo { void foo(int,int); }; struct direct_const_foo { void foo(int,int) const; }; struct inherited_const_foo : public direct_const_foo { }; // Now anywhere in your code you can seamlessly use // the foo_bar function on any object: void test() { int a; foo_bar(a); // calls default_foo empty b; foo_bar(b); // calls default_foo direct_foo c; foo_bar(c); // calls default_foo (member function is not const) direct_const_foo d; foo_bar(d); // calls d.foo (member function is const) inherited_const_foo e; foo_bar(e); // calls e.foo (inherited member function) } |
。
型
您可以使用std::is_member_function_pointer
1 2 3 4 5 6 | class A { public: void foo() {}; } bool test = std::is_member_function_pointer<decltype(&A::foo)>::value; |
型
为了不受干扰,还可以通过Koenig查找将
打开任何给定的名称空间来实现自由函数都是错误的。(例如,您不应该为自己的类型打开命名空间
型
可以。第二次尝试。如果你也不喜欢这个,没关系,我在找更多的主意。
Herb Sutter的文章谈到了特性。因此,您可以有一个traits类,它的默认实例化具有回退行为,对于存在成员函数的每个类,traits类都专门用于调用成员函数。我相信Herb的文章提到了这样做的一种技巧,这样就不需要大量的复制和粘贴。
不过,正如我所说的,也许您不希望在实现该成员的"标记"类中进行额外的工作。在这种情况下,我正在寻找第三种解决方案……
没有C + +的支持(11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <iostream> using namespace std; struct A { void foo(void); }; struct Aa: public A { }; struct B { }; struct retA { int foo(void); }; struct argA { void foo(double); }; struct constA { void foo(void) const; }; struct varA { int foo; }; template<typename T> struct FooFinder { typedef char true_type[1]; typedef char false_type[2]; template<int> struct TypeSink; template<class U> static true_type &match(U); template<class U> static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *); template<class U> static false_type &test(...); enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) }; }; int main() { cout << FooFinder<A>::value << endl; cout << FooFinder<Aa>::value << endl; cout << FooFinder::value << endl; cout << FooFinder<retA>::value << endl; cout << FooFinder::value << endl; cout << FooFinder<constA>::value << endl; cout << FooFinder<varA>::value << endl; } |
恩hopefully工程知识
茶和茶
一sfinae
to be to the
如果你使用Facebook,他们放开,are out of the box宏帮助你:P></
1 2 3 4 5 6 7 8 9 | #include <folly/Traits.h> namespace { FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test); } // unnamed-namespace void some_func() { cout <<"Does class Foo have a member int test() const?" << boolalpha << has_test_traits<Foo, int() const>::value; } |
虽然is the same the implementation details with the previous答案是简单的内部,使用图书馆。P></
型
我相信你要找的答案就在这里。
http://www.martinecker.com/wiki/index.php?title=在编译时检测u运算符的存在
这里有一个稍微充实一点的例子
http://pastie.org/298994
我使用这项技术来检测相关类上支持的Ostream运算符的存在,然后根据不同的代码位生成不同的代码。
我不相信在找到关联的解决方案之前是可能的,但这是一个非常巧妙的技巧。花时间理解代码,这是非常值得的。
布拉德