是否可以编写一个模板来检查函数的存在?

是否有可能编写一个模板来改变行为取决于某个成员函数是否定义在一个类上?

以下是我想写的一个简单例子:

template<class T> std::string optionalToString(T* obj) { if (FUNCTION_EXISTS(T->toString)) return obj->toString(); else return "toString not defined"; } 

所以,如果class TtoString()定义,那么它使用它; 否则,它不。 我不知道怎么做的神奇的部分是“FUNCTION_EXISTS”部分。

是的,用SFINAE你可以检查给定的类是否提供了某种方法。 这是工作代码:

 #include <iostream> struct Hello { int helloworld() { return 0; } }; struct Generic {}; // SFINAE test template <typename T> class has_helloworld { typedef char one; typedef long two; template <typename C> static one test( typeof(&C::helloworld) ) ; template <typename C> static two test(...); public: enum { value = sizeof(test<T>(0)) == sizeof(char) }; }; int main(int argc, char *argv[]) { std::cout << has_helloworld<Hello>::value << std::endl; std::cout << has_helloworld<Generic>::value << std::endl; return 0; } 

我刚刚用Linux和gcc 4.1 / 4.3testing过。 我不知道它是否可以移植到其他运行不同编译器的平台上。

这个问题是旧的,但是用C ++ 11,我们得到了一个新的方法来检查一个函数的存在(或者真的存在任何非types成员),再次依靠SFINAE:

 template<class T> auto serialize_imp(std::ostream& os, T const& obj, int) -> decltype(os << obj, void()) { os << obj; } template<class T> auto serialize_imp(std::ostream& os, T const& obj, long) -> decltype(obj.stream(os), void()) { obj.stream(os); } template<class T> auto serialize(std::ostream& os, T const& obj) -> decltype(serialize_imp(os, obj, 0), void()) { serialize_imp(os, obj, 0); } 

现场示例。

现在进行一些解释。 首先,如果decltype的第一个expression式无效(也就是说,该函数不存在),则使用expression式SFINAE从重载parsing中排除serialize(_imp)函数。

void()用于使所有这些函数的返回typesvoid

如果两个参数都可用,则使用0参数来优先selectos << obj重载(文字0的types为int ,因此第一个重载是更好的匹配)。


现在,你可能需要一个特性来检查一个函数是否存在。 幸运的是,写这个很容易。 但是,请注意,您需要自己为每个不同的函数名称编写一个特征。

 #include <type_traits> template<class> struct sfinae_true : std::true_type{}; namespace detail{ template<class T, class A0> static auto test_stream(int) -> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>; template<class, class A0> static auto test_stream(long) -> std::false_type; } // detail:: template<class T, class Arg> struct has_stream : decltype(detail::test_stream<T, Arg>(0)){}; 

现场示例。

并进行解释。 首先, sfinae_true是一个辅助types,基本上与写入decltype(void(std::declval<T>().stream(a0)), std::true_type{}) 。 好处就是它更短。
接下来, struct has_stream : decltype(...) std::true_typestd::true_typestd::false_typeinheritance,具体取决于std::false_type中的decltype检查是否失败。
最后, std::declval给你一个你通过的任何types的“值”,而不需要知道如何构造它。 请注意,这只能在未经评估的上下文中进行,例如decltypesizeof等。


请注意, decltype不一定是必需的,因为sizeof (以及所有未评估的上下文)都得到了增强。 这只是decltype已经提供了一个types,因此只是更干净。 这是一个重载的sizeof版本:

 template<class T> void serialize_imp(std::ostream& os, T const& obj, int, int(*)[sizeof((os << obj),0)] = 0) { os << obj; } 

现场示例。

intlong参数仍然出于同样的原因。 数组指针用于提供可以使用sizeof的上下文。

C ++允许使用SFINAE (请注意,使用C ++ 11的特性是简单的,因为它支持在几乎任意的expression式上扩展SFINAE–下面的内容是为通用的C ++ 03编译器而devise的):

 #define HAS_MEM_FUNC(func, name) \ template<typename T, typename Sign> \ struct name { \ typedef char yes[1]; \ typedef char no [2]; \ template <typename U, U> struct type_check; \ template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \ template <typename > static no &chk(...); \ static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \ } 

上面的模板和macros试图实例化一个模板,给它一个成员函数指针types,以及实际的成员函数指针。 如果types不适合,SFINAE导致模板被忽略。 像这样的用法:

 HAS_MEM_FUNC(toString, has_to_string); template<typename T> void doSomething() { if(has_to_string<T, std::string(T::*)()>::value) { ... } else { ... } } 

但是请注意,你不能只在那个分支中调用那个toString函数。 因为编译器会检查两个分支的有效性,如果这个函数不存在的话就会失败。 一种方法是再次使用SFINAE(enable_if也可以从boost获得):

 template<bool C, typename T = void> struct enable_if { typedef T type; }; template<typename T> struct enable_if<false, T> { }; HAS_MEM_FUNC(toString, has_to_string); template<typename T> typename enable_if<has_to_string<T, std::string(T::*)()>::value, std::string>::type doSomething(T * t) { /* something when T has toString ... */ return t->toString(); } template<typename T> typename enable_if<!has_to_string<T, std::string(T::*)()>::value, std::string>::type doSomething(T * t) { /* something when T doesnt have toString ... */ return "T::toString() does not exist."; } 

玩得开心使用它。 它的优点是它也适用于重载的成员函数,也适用于const成员函数(记得使用std::string(T::*)() const作为成员函数指针types,然后!)。

虽然这个问题已经两年了,但我敢于补充我的答案。 希望它能澄清以前无可争议的优秀解决scheme。 我采取了Nicola Bonelli和Johannes Schaub非常有帮助的答案,并将它们合并成一个解决scheme,即恕我直言,更易读,清晰,不需要扩展types:

 template <class Type> class TypeHasToString { // This type won't compile if the second template parameter isn't of type T, // so I can put a function pointer type in the first parameter and the function // itself in the second thus checking that the function has a specific signature. template <typename T, T> struct TypeCheck; typedef char Yes; typedef long No; // A helper struct to hold the declaration of the function pointer. // Change it if the function signature changes. template <typename T> struct ToString { typedef void (T::*fptr)(); }; template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*); template <typename T> static No HasToString(...); public: static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes)); }; 

我用gcc 4.1.2查了一下。 信贷主要是尼古拉Bonelli和约翰Schaub,所以给他们一个投票,如果我的回答可以帮助你:)

检测工具包

N4502提出了一个包含在C ++ 17标准库中的检测工具,可以以一种很好的方式解决问题。 而且,它刚刚被录入图书馆基础知识TS v2。 它引入了一些元函数,包括std::is_detected ,它可以用来轻松地在其上面写入types或函数检测元函数。 这里是你如何使用它:

 template<typename T> using toString_t = decltype( std::declval<T&>().toString() ); template<typename T> constexpr bool has_toString = std::is_detected_v<toString_t, T>; 

请注意,以上示例未经testing。 检测工具包在标准库中尚不可用,但提案包含一个完整的实现,如果您确实需要,可以轻松地进行复制。 if constexpr它可以很好地与C ++ 17特性结合if constexpr

 template<class T> std::string optionalToString(T* obj) { if constexpr (has_toString<T>) return obj->toString(); else return "toString not defined"; } 

Boost.TTI

在Boost 1.54.0中引入了Boost.TTI来执行这种检查的另一个有点习惯性的工具包 – 尽pipe不那么优雅。 对于你的例子,你将不得不使用macrosBOOST_TTI_HAS_MEMBER_FUNCTION 。 这里是你如何使用它:

 #include <boost/tti/has_member_function.hpp> // Generate the metafunction BOOST_TTI_HAS_MEMBER_FUNCTION(toString) // Check whether T has a member function toString // which takes no parameter and returns a std::string constexpr bool foo = has_member_function_toString<T, std::string>::value; 

然后,你可以使用bool创build一个SFINAE检查。

说明

macrosBOOST_TTI_HAS_MEMBER_FUNCTION生成元函数has_member_function_toString ,它将选中的types作为其第一个模板参数。 第二个模板参数对应于成员函数的返回types,下面的参数对应于函数参数的types。 如果类T有成员函数std::string toString()则成员value包含true

或者, has_member_function_toString可以将成员函数指针作为模板参数。 因此,可以用has_member_function_toString<std::string T::* ()>::valuereplacehas_member_function_toString<T, std::string>::value has_member_function_toString<std::string T::* ()>::value

这是什么types的特征。 不幸的是,他们必须手动定义。 在你的情况下,想象一下:

 template <typename T> struct response_trait { static bool const has_tostring = false; }; template <> struct response_trait<your_type_with_tostring> { static bool const has_tostring = true; } 

一个简单的C ++ 11解决scheme:

 template<class T> auto optionalToString(T* obj) -> decltype( obj->toString() ) { return obj->toString(); } auto optionalToString(...) -> string { return "toString not defined"; } 

那么,这个问题已经有很长的答案了,但是我想强调一下Morwenn的评论:有一个C ++ 17的提议使得它更加简单。 有关详细信息,请参阅N4502 ,但作为一个独立的示例,请考虑以下内容。

这部分是不变的部分,把它放在一个标题。

 // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf. template <typename...> using void_t = void; // Primary template handles all types not supporting the operation. template <typename, template <typename> class, typename = void_t<>> struct detect : std::false_type {}; // Specialization recognizes/validates only types supporting the archetype. template <typename T, template <typename> class Op> struct detect<T, Op, void_t<Op<T>>> : std::true_type {}; 

那么就有可变部分,在那里指定你要找的东西(types,成员types,函数,成员函数等等)。 在OP的情况下:

 template <typename T> using toString_t = decltype(std::declval<T>().toString()); template <typename T> using has_toString = detect<T, toString_t>; 

下面的例子取自N4502 ,显示了一个更详细的探测:

 // Archetypal expression for assignment operation. template <typename T> using assign_t = decltype(std::declval<T&>() = std::declval<T const &>()) // Trait corresponding to that archetype. template <typename T> using is_assignable = detect<T, assign_t>; 

与上面描述的其他实现相比,这个实现相当简单:只需要​​一套简化的工具( void_tdetect )就足够了,不需要void_tmacros。 此外,据报道(参见N4502 ),与以前的方法相比,它显着地更有效(编译时和编译器内存消耗)。

这是一个生动的例子 。 它与Clang一起工作良好,但不幸的是,5.1之前的GCC版本遵循了C ++ 11标准的不同解释,导致void_t无法按预期工作。 Yakk已经提供了解决方法:使用void_t ( void_t在参数列表中的作品,但不作为返回types )的以下定义:

 #if __GNUC__ < 5 && ! defined __clang__ // https://stackoverflow.com/a/28967049/1353549 template <typename...> struct voider { using type = void; }; template <typename...Ts> using void_t = typename voider<Ts...>::type; #else template <typename...> using void_t = void; #endif 

以下是一些使用片段:*所有这些的胆量越来越低

检查给定类中的成员x 可以是var,func,class,union或者enum:

 CREATE_MEMBER_CHECK(x); bool has_x = has_member_x<class_to_check_for_x>::value; 

检查成员函数void x()

 //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; 

检查成员variablesx

 CREATE_MEMBER_VAR_CHECK(x); bool has_var_x = has_member_var_x<class_to_check_for_x>::value; 

检查会员类别x

 CREATE_MEMBER_CLASS_CHECK(x); bool has_class_x = has_member_class_x<class_to_check_for_x>::value; 

检查会员联盟x

 CREATE_MEMBER_UNION_CHECK(x); bool has_union_x = has_member_union_x<class_to_check_for_x>::value; 

检查成员枚举x

 CREATE_MEMBER_ENUM_CHECK(x); bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value; 

无论签名如何,检查任何成员函数x

 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; 

要么

 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; 

细节和核心:

 /* - 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; }; 

macros(El Diablo!):

CREATE_MEMBER_CHECK:

 //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<ambiguate<T, AmbiguitySeed_##member>> \ , Alias_##member<AmbiguitySeed_##member> \ >::value \ ; \ } 

CREATE_MEMBER_VAR_CHECK:

 //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 {} 

CREATE_MEMBER_FUNC_SIG_CHECK:

 //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 {} 

CREATE_MEMBER_CLASS_CHECK:

 //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 {} 

CREATE_MEMBER_UNION_CHECK:

 //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 {} 

CREATE_MEMBER_ENUM_CHECK:

 //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 {} 

CREATE_MEMBER_FUNC_CHECK:

 //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 \ ; \ } 

CREATE_MEMBER_CHECKS:

 //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) 

如果方法碰巧在基类中定义,那么在这里提供的标准C ++解决scheme将无法按预期工作。

对于处理这种情况的解决scheme,请参阅:

在俄文: http : //www.rsdn.ru/forum/message/2759773.1.aspx

Roman.Perepelitsa的英文翻译: http ://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

它非常聪明。 然而,这个解决scheme的一个问题是,如果被testing的types是不能用作基类的编译器错误(例如原始types)

在Visual Studio中,我注意到如果使用没有参数的方法,需要在参数周围插入一对额外的冗余()以在sizeofexpression式中推演()。

我在另一个线程中写了一个答案(与上面的解决scheme不同),它也检查inheritance的成员函数:

SFINAE检查inheritance的成员函数

以下是该解决scheme的一些示例:

例1:

我们正在检查具有以下签名的成员: T::const_iterator begin() const

 template<class T> struct has_const_begin { typedef char (&Yes)[1]; typedef char (&No)[2]; template<class U> static Yes test(U const * data, typename std::enable_if<std::is_same< typename U::const_iterator, decltype(data->begin()) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0)); }; 

请注意,它甚至检查方法的常量,并使用原始types。 (我的意思是has_const_begin<int>::value是false,不会导致编译时错误。)

例2

现在我们正在寻找签名: void foo(MyClass&, unsigned)

 template<class T> struct has_foo { typedef char (&Yes)[1]; typedef char (&No)[2]; template<class U> static Yes test(U * data, MyClass* arg1 = 0, typename std::enable_if<std::is_void< decltype(data->foo(*arg1, 1u)) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0)); }; 

请注意,MyClass不一定是默认的可构造的或满足任何特殊的概念。 该技术也适用于模板成员。

我急切地等待关于这个的意见。

现在这是一个很好的小谜题 – 很好的问题!

这是Nicola Bonelli解决scheme的另一种select,它不依赖于非标准types的操作符。

不幸的是,它不能在GCC(MinGW)3.4.5或Digital Mars 8.42n上运行,但是它可以在所有版本的MSVC(包括VC6)和Comeau C ++上运行。

较长的评论块具有如何工作(或应该工作)的细节。 正如它所说的,我不确定哪个行为符合标准 – 我很欢迎评论。


更新 – 2008年11月7日:

看起来像这段代码在语法上是正确的,MSVC和Comeau C ++显示的行为不符合标准(感谢Leon Timmermans和litb指出我在正确的方向)。 C ++ 03标准说明如下:

14.6.2相关名称[temp.dep]

第3段

在类模板或类模板成员的定义中,如果类模板的基类依赖于模板参数,则在类的定义点处不进行非限定名称查找模板或成员或在类模板或成员的实例化过程中。

所以,当MSVC或Comeau考虑在模板实例化时,在doToString()的调用站点执行名称查找的toString()成员函数时,这是不正确的(即使它实际上是我正在寻找的行为在这种情况下)。

The behavior of GCC and Digital Mars looks to be correct – in both cases the non-member toString() function is bound to the call.

Rats – I thought I might have found a clever solution, instead I uncovered a couple compiler bugs…


 #include <iostream> #include <string> struct Hello { std::string toString() { return "Hello"; } }; struct Generic {}; // the following namespace keeps the toString() method out of // most everything - except the other stuff in this // compilation unit namespace { std::string toString() { return "toString not defined"; } template <typename T> class optionalToStringImpl : public T { public: std::string doToString() { // in theory, the name lookup for this call to // toString() should find the toString() in // the base class T if one exists, but if one // doesn't exist in the base class, it'll // find the free toString() function in // the private namespace. // // This theory works for MSVC (all versions // from VC6 to VC9) and Comeau C++, but // does not work with MinGW 3.4.5 or // Digital Mars 8.42n // // I'm honestly not sure what the standard says // is the correct behavior here - it's sort // of like ADL (Argument Dependent Lookup - // also known as Koenig Lookup) but without // arguments (except the implied "this" pointer) return toString(); } }; } template <typename T> std::string optionalToString(T & obj) { // ugly, hacky cast... optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj); return temp->doToString(); } int main(int argc, char *argv[]) { Hello helloObj; Generic genericObj; std::cout << optionalToString( helloObj) << std::endl; std::cout << optionalToString( genericObj) << std::endl; return 0; } 

This is a C++11 solution for the general problem if "If I did X, would it compile?"

 template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void` template<class T> using type_sink_t = typename type_sink<T>::type; template<class T, class=void> struct has_to_string : std::false_type {}; \ template<class T> struct has_to_string< T, type_sink_t< decltype( std::declval<T>().toString() ) > >: std::true_type {}; 

Trait has_to_string such that has_to_string<T>::value is true if and only if T has a method .toString that can be invoked with 0 arguments in this context.

Next, I'd use tag dispatching:

 namespace details { template<class T> std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) { return obj->toString(); } template<class T> std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) { return "toString not defined"; } } template<class T> std::string optionalToString(T* obj) { return details::optionalToString_helper( obj, has_to_string<T>{} ); } 

which tends to be more maintainable than complex SFINAE expressions.

You can write these traits with a macro if you find yourself doing it alot, but they are relatively simple (a few lines each) so maybe not worth it:

 #define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \ template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \ template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {}; 

what the above does is create a macro MAKE_CODE_TRAIT . You pass it the name of the trait you want, and some code that can test the type T . 从而:

 MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() ) 

creates the above traits class.

As an aside, the above technique is part of what MS calls "expression SFINAE", and their 2013 compiler fails pretty hard.

Note that in C++1y the following syntax is possible:

 template<class T> std::string optionalToString(T* obj) { return compiled_if< has_to_string >(*obj, [&](auto&& obj) { return obj.toString(); }) *compiled_else ([&]{ return "toString not defined"; }); } 

which is an inline compilation conditional branch that abuses lots of C++ features. Doing so is probably not worth it, as the benefit (of code being inline) is not worth the cost (of next to nobody understanding how it works), but the existence of that above solution may be of interest.

MSVC has the __if_exists and __if_not_exists keywords ( Doc ). Together with the typeof-SFINAE approach of Nicola I could create a check for GCC and MSVC like the OP looked for.

Update: Source can be found Here

I modified the solution provided in https://stackoverflow.com/a/264088/2712152 to make it a bit more general. Also since it doesn't use any of the new C++11 features we can use it with old compilers and should also work with msvc. But the compilers should enable C99 to use this since it uses variadic macros.

The following macro can be used to check if a particular class has a particular typedef or not.

 /** * @class : HAS_TYPEDEF * @brief : This macro will be used to check if a class has a particular * typedef or not. * @param typedef_name : Name of Typedef * @param name : Name of struct which is going to be run the test for * the given particular typedef specified in typedef_name */ #define HAS_TYPEDEF(typedef_name, name) \ template <typename T> \ struct name { \ typedef char yes[1]; \ typedef char no[2]; \ template <typename U> \ struct type_check; \ template <typename _1> \ static yes& chk(type_check<typename _1::typedef_name>*); \ template <typename> \ static no& chk(...); \ static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \ } 

The following macro can be used to check if a particular class has a particular member function or not with any given number of arguments.

 /** * @class : HAS_MEM_FUNC * @brief : This macro will be used to check if a class has a particular * member function implemented in the public section or not. * @param func : Name of Member Function * @param name : Name of struct which is going to be run the test for * the given particular member function name specified in func * @param return_type: Return type of the member function * @param ellipsis(...) : Since this is macro should provide test case for every * possible member function we use variadic macros to cover all possibilities */ #define HAS_MEM_FUNC(func, name, return_type, ...) \ template <typename T> \ struct name { \ typedef return_type (T::*Sign)(__VA_ARGS__); \ typedef char yes[1]; \ typedef char no[2]; \ template <typename U, U> \ struct type_check; \ template <typename _1> \ static yes& chk(type_check<Sign, &_1::func>*); \ template <typename> \ static no& chk(...); \ static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \ } 

We can use the above 2 macros to perform the checks for has_typedef and has_mem_func as:

 class A { public: typedef int check; void check_function() {} }; class B { public: void hello(int a, double b) {} void hello() {} }; HAS_MEM_FUNC(check_function, has_check_function, void, void); HAS_MEM_FUNC(hello, hello_check, void, int, double); HAS_MEM_FUNC(hello, hello_void_check, void, void); HAS_TYPEDEF(check, has_typedef_check); int main() { std::cout << "Check Function A:" << has_check_function<A>::value << std::endl; std::cout << "Check Function B:" << has_check_function<B>::value << std::endl; std::cout << "Hello Function A:" << hello_check<A>::value << std::endl; std::cout << "Hello Function B:" << hello_check<B>::value << std::endl; std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl; std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl; std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl; std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl; } 

Strange nobody suggested the following nice trick I saw once on this very site :

 template <class T> struct has_foo { struct S { void foo(...); }; struct derived : S, T {}; template <typename V, V> struct W {}; template <typename X> char (&test(W<void (X::*)(), &X::foo> *))[1]; template <typename> char (&test(...))[2]; static const bool value = sizeof(test<derived>(0)) == 1; }; 

You have to make sure T is a class. It seems that ambiguity in the lookup of foo is a substitution failure. I made it work on gcc, not sure if it is standard though.

The generic template that can be used for checking if some "feature" is supported by the type:

 #include <type_traits> template <template <typename> class TypeChecker, typename Type> struct is_supported { // these structs are used to recognize which version // of the two functions was chosen during overload resolution struct supported {}; struct not_supported {}; // this overload of chk will be ignored by SFINAE principle // if TypeChecker<Type_> is invalid type template <typename Type_> static supported chk(typename std::decay<TypeChecker<Type_>>::type *); // ellipsis has the lowest conversion rank, so this overload will be // chosen during overload resolution only if the template overload above is ignored template <typename Type_> static not_supported chk(...); // if the template overload of chk is chosen during // overload resolution then the feature is supported // if the ellipses overload is chosen the the feature is not supported static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value; }; 

The template that checks whether there is a method foo that is compatible with signature double(const char*)

 // if T doesn't have foo method with the signature that allows to compile the bellow // expression then instantiating this template is Substitution Failure (SF) // which Is Not An Error (INAE) if this happens during overload resolution template <typename T> using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>()))); 

例子

 // types that support has_foo struct struct1 { double foo(const char*); }; // exact signature match struct struct2 { int foo(const std::string &str); }; // compatible signature struct struct3 { float foo(...); }; // compatible ellipsis signature struct struct4 { template <typename T> int foo(T t); }; // compatible template signature // types that do not support has_foo struct struct5 { void foo(const char*); }; // returns void struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double struct struct7 { double foo( int *); }; // const char* can't be converted to int* struct struct8 { double bar(const char*); }; // there is no foo method int main() { std::cout << std::boolalpha; std::cout << is_supported<has_foo, int >::value << std::endl; // false std::cout << is_supported<has_foo, double >::value << std::endl; // false std::cout << is_supported<has_foo, struct1>::value << std::endl; // true std::cout << is_supported<has_foo, struct2>::value << std::endl; // true std::cout << is_supported<has_foo, struct3>::value << std::endl; // true std::cout << is_supported<has_foo, struct4>::value << std::endl; // true std::cout << is_supported<has_foo, struct5>::value << std::endl; // false std::cout << is_supported<has_foo, struct6>::value << std::endl; // false std::cout << is_supported<has_foo, struct7>::value << std::endl; // false std::cout << is_supported<has_foo, struct8>::value << std::endl; // false return 0; } 

http://coliru.stacked-crooked.com/a/83c6a631ed42cea4

There are a lot of answers here, but I failed, to find a version, that performs real method resolution ordering, while not using any of the newer c++ features (only using c++98 features).
Note: This version is tested and working with vc++2013, g++ 5.2.0 and the onlline compiler.

So I came up with a version, that only uses sizeof():

 template<typename T> T declval(void); struct fake_void { }; template<typename T> T &operator,(T &,fake_void); template<typename T> T const &operator,(T const &,fake_void); template<typename T> T volatile &operator,(T volatile &,fake_void); template<typename T> T const volatile &operator,(T const volatile &,fake_void); struct yes { char v[1]; }; struct no { char v[2]; }; template<bool> struct yes_no:yes{}; template<> struct yes_no<false>:no{}; template<typename T> struct has_awesome_member { template<typename U> static yes_no<(sizeof(( declval<U>().awesome_member(),fake_void() ))!=0)> check(int); template<typename> static no check(...); enum{value=sizeof(check<T>(0)) == sizeof(yes)}; }; struct foo { int awesome_member(void); }; struct bar { }; struct foo_void { void awesome_member(void); }; struct wrong_params { void awesome_member(int); }; static_assert(has_awesome_member<foo>::value,""); static_assert(!has_awesome_member<bar>::value,""); static_assert(has_awesome_member<foo_void>::value,""); static_assert(!has_awesome_member<wrong_params>::value,""); 

Live demo (with extended return type checking and vc++2010 workaround): http://cpp.sh/5b2vs

No source, as I came up with it myself.

When running the Live demo on the g++ compiler, please note that array sizes of 0 are allowed, meaning that the static_assert used will not trigger a compiler error, even when it fails.
A commonly used work-around is to replace the 'typedef' in the macro with 'extern'.

How about this solution?

 #include <type_traits> template <typename U, typename = void> struct hasToString : std::false_type { }; template <typename U> struct hasToString<U, typename std::enable_if<bool(sizeof(&U::toString))>::type > : std::true_type { }; 

Here is my version that handles all possible member function overloads with arbitrary arity, including template member functions, possibly with default arguments. It distinguishes 3 mutually exclusive scenarios when making a member function call to some class type, with given arg types: (1) valid, or (2) ambiguous, or (3) non-viable. 用法示例:

 #include <string> #include <vector> HAS_MEM(bar) HAS_MEM_FUN_CALL(bar) struct test { void bar(int); void bar(double); void bar(int,double); template < typename T > typename std::enable_if< not std::is_integral<T>::value >::type bar(const T&, int=0){} template < typename T > typename std::enable_if< std::is_integral<T>::value >::type bar(const std::vector<T>&, T*){} template < typename T > int bar(const std::string&, int){} }; 

Now you can use it like this:

 int main(int argc, const char * argv[]) { static_assert( has_mem_bar<test>::value , ""); static_assert( has_valid_mem_fun_call_bar<test(char const*,long)>::value , ""); static_assert( has_valid_mem_fun_call_bar<test(std::string&,long)>::value , ""); static_assert( has_valid_mem_fun_call_bar<test(std::vector<int>, int*)>::value , ""); static_assert( has_no_viable_mem_fun_call_bar<test(std::vector<double>, double*)>::value , ""); static_assert( has_valid_mem_fun_call_bar<test(int)>::value , ""); static_assert( std::is_same<void,result_of_mem_fun_call_bar<test(int)>::type>::value , ""); static_assert( has_valid_mem_fun_call_bar<test(int,double)>::value , ""); static_assert( not has_valid_mem_fun_call_bar<test(int,double,int)>::value , ""); static_assert( not has_ambiguous_mem_fun_call_bar<test(double)>::value , ""); static_assert( has_ambiguous_mem_fun_call_bar<test(unsigned)>::value , ""); static_assert( has_viable_mem_fun_call_bar<test(unsigned)>::value , ""); static_assert( has_viable_mem_fun_call_bar<test(int)>::value , ""); static_assert( has_no_viable_mem_fun_call_bar<test(void)>::value , ""); return 0; } 

Here is the code, written in c++11, however, you can easily port it (with minor tweaks) to non-c++11 that has typeof extensions (eg gcc). You can replace the HAS_MEM macro with your own.

 #pragma once #if __cplusplus >= 201103 #include <utility> #include <type_traits> #define HAS_MEM(mem) \ \ template < typename T > \ struct has_mem_##mem \ { \ struct yes {}; \ struct no {}; \ \ struct ambiguate_seed { char mem; }; \ template < typename U > struct ambiguate : U, ambiguate_seed {}; \ \ template < typename U, typename = decltype(&U::mem) > static constexpr no test(int); \ template < typename > static constexpr yes test(...); \ \ static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ; \ typedef std::integral_constant<bool,value> type; \ }; #define HAS_MEM_FUN_CALL(memfun) \ \ template < typename Signature > \ struct has_valid_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_valid_mem_fun_call_##memfun< T(Args...) > \ { \ struct yes {}; \ struct no {}; \ \ template < typename U, bool = has_mem_##memfun<U>::value > \ struct impl \ { \ template < typename V, typename = decltype(std::declval<V>().memfun(std::declval<Args>()...)) > \ struct test_result { using type = yes; }; \ \ template < typename V > static constexpr typename test_result<V>::type test(int); \ template < typename > static constexpr no test(...); \ \ static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value; \ using type = std::integral_constant<bool, value>; \ }; \ \ template < typename U > \ struct impl<U,false> : std::false_type {}; \ \ static constexpr bool value = impl<T>::value; \ using type = std::integral_constant<bool, value>; \ }; \ \ template < typename Signature > \ struct has_ambiguous_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_ambiguous_mem_fun_call_##memfun< T(Args...) > \ { \ struct ambiguate_seed { void memfun(...); }; \ \ template < class U, bool = has_mem_##memfun<U>::value > \ struct ambiguate : U, ambiguate_seed \ { \ using ambiguate_seed::memfun; \ using U::memfun; \ }; \ \ template < class U > \ struct ambiguate<U,false> : ambiguate_seed {}; \ \ static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate<T>(Args...) >::value; \ using type = std::integral_constant<bool, value>; \ }; \ \ template < typename Signature > \ struct has_viable_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_viable_mem_fun_call_##memfun< T(Args...) > \ { \ static constexpr bool value = has_valid_mem_fun_call_##memfun<T(Args...)>::value \ or has_ambiguous_mem_fun_call_##memfun<T(Args...)>::value; \ using type = std::integral_constant<bool, value>; \ }; \ \ template < typename Signature > \ struct has_no_viable_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_no_viable_mem_fun_call_##memfun < T(Args...) > \ { \ static constexpr bool value = not has_viable_mem_fun_call_##memfun<T(Args...)>::value; \ using type = std::integral_constant<bool, value>; \ }; \ \ template < typename Signature > \ struct result_of_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct result_of_mem_fun_call_##memfun< T(Args...) > \ { \ using type = decltype(std::declval<T>().memfun(std::declval<Args>()...)); \ }; #endif 

You can skip all the metaprogramming in C++14, and just write this using fit::conditional from the Fit library:

 template<class T> std::string optionalToString(T* x) { return fit::conditional( [](auto* obj) -> decltype(obj->toString()) { return obj->toString(); }, [](auto*) { return "toString not defined"; } )(x); } 

You can also create the function directly from the lambdas as well:

 FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional( [](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); }, [](auto*) -> std::string { return "toString not defined"; } ); 

However, if you are using a compiler that doesn't support generic lambdas, you will have to write separate function objects:

 struct withToString { template<class T> auto operator()(T* obj) const -> decltype(obj->toString(), std::string()) { return obj->toString(); } }; struct withoutToString { template<class T> std::string operator()(T*) const { return "toString not defined"; } }; FIT_STATIC_FUNCTION(optionalToString) = fit::conditional( withToString(), withoutToString() ); 

Here is an example of the working code.

 template<typename T> using toStringFn = decltype(std::declval<const T>().toString()); template <class T, toStringFn<T>* = nullptr> std::string optionalToString(const T* obj, int) { return obj->toString(); } template <class T> std::string optionalToString(const T* obj, long) { return "toString not defined"; } int main() { A* a; B* b; std::cout << optionalToString(a, 0) << std::endl; // This is A std::cout << optionalToString(b, 0) << std::endl; // toString not defined } 

toStringFn<T>* = nullptr will enable the function which takes extra int argument which has a priority over function which takes long when called with 0 .

You can use the same principle for the functions which returns true if function is implemented.

 template <typename T> constexpr bool toStringExists(long) { return false; } template <typename T, toStringFn<T>* = nullptr> constexpr bool toStringExists(int) { return true; } int main() { A* a; B* b; std::cout << toStringExists<A>(0) << std::endl; // true std::cout << toStringExists<B>(0) << std::endl; // false }