When writing template code, being able to detect whether a type has a method with a given signature may be very useful. For example, when writing a custom buffer, checking for T::size() may allow us to preallocate memory in an input function. Checking for T::operator bool() will tell us whether our type is testable without writing specializations for every such type, which would be especially tedious for unnamed types. (side note: lambdas don’t have operator bool(), but they’re implicitly convertible to function pointers when their capture list is empty, which should make them testable, but they aren’t in VC 10 and 11 )

Achieving the desired result requires basic understanding of SFINAE and overload resolution. Let’s say we want to check for existence of ::size() as used in the standard library or in the codebase of Qt, that is size_t (T::*)() const and int (T::*)() const respectively.



The naïve implementation

template < typename T > struct has_size_method { private : typedef std :: true_type yes ; typedef std :: false_type no ; template < typename U, int ( U :: * f ) ( ) const > struct SFINAE { } ; template < typename U, std :: size_t ( U :: * f ) ( ) const > struct SFINAE < U,f > { } ; template < class C > static yes test ( SFINAE < C, & C :: size > * ) ; template < class C > static no test ( ... ) ; public : static constexpr bool value = std :: is_same < yes, decltype ( test < T > ( nullptr ) ) > :: value ; } ; template<typename T> struct has_size_method { private: typedef std::true_type yes; typedef std::false_type no; template<typename U, int (U::*f)() const> struct SFINAE{}; template<typename U, std::size_t (U::*f)() const> struct SFINAE<U,f>{}; template<class C> static yes test(SFINAE<C,&C::size>*); template<class C> static no test(...); public: static constexpr bool value = std::is_same<yes,decltype(test<T>(nullptr))>::value; };

Create a metafunction and use SFINAE to disable test returning yes if no match is found. Simple and effective, or is it? Unfortunately not. Consider x and y in the following code:

struct x { int size ( ) const { return 42 ; } } ; struct y : x { } ; cout << has_size_method < x > :: value << ", " << has_size_method < y > :: value << endl ; struct x { int size() const { return 42; } }; struct y : x {}; cout << has_size_method<x>::value << ", " << has_size_method<y>::value << endl;

It will print “1, 0”, because SFINAE disables the SFINAE<C,&C::size>* overload if the desired function exists in C‘s parent class. Here’s a full example.

The real deal A partial fix

To avoid the above problem, the implementation can’t use &C::size (or any desired function name) as a template parameter, and the check needs to be split into two parts: checking whether size is a member of T and checking whether it has an acceptable signature.

template < typename T > struct has_size_method { private : typedef std :: true_type yes ; typedef std :: false_type no ; template < typename C > static yes test ( int ( C :: * f ) ( ) const ) { return yes { } ; } template < typename C > static yes test ( size_t ( C :: * f ) ( ) ) { return yes { } ; } template < typename C > static auto test ( decltype ( & C :: size ) , void * ) - > decltype ( test ( & C :: size ) ) { return test ( & C :: size ) ; } template < typename C > static no test ( ... ) { return no { } ; } public : static constexpr bool value = std :: is_same < decltype ( test < T > ( 0 , 0 ) ) ,yes > :: value ; } ; template<typename T> struct has_size_method { private: typedef std::true_type yes; typedef std::false_type no; template<typename C> static yes test(int (C::*f)() const){ return yes{}; } template<typename C> static yes test(size_t (C::*f)()){ return yes{}; } template<typename C> static auto test(decltype(&C::size),void*) -> decltype(test(&C::size)){ return test(&C::size); } template<typename C> static no test(...){ return no{}; } public: static constexpr bool value = std::is_same<decltype(test<T>(0,0)),yes>::value; };

It works correctly in all most required cases and can be easily modified to account for different signatures. If constexpr was changed to const (as VC11 with CTP doesn’t seem to recognize this keyword) the above code would compile and work correctly on GCC, clang and MSVC11. If you’re bound to support VS11 this is the best you can do. This solution was inspired by Mike Kinghan’s answer on stackoverflow and, even though the it is not perfect, I recommend reading his post there.

The real real deal

After this was posted on reddit, STL pointed out (thanks!) that that there is a vastly superior solution to this problem – Expression SFINAE. It allows not only cleaner code, but also clear stating of the intent by showing the usage (although that may cause some implementation details to leak out if you’re not careful).

template < typename T > struct has_size_method { private : typedef std :: true_type yes ; typedef std :: false_type no ; template < typename U > static auto test ( int ) - > decltype ( std :: declval < U > ( ) . size ( ) == 1 , yes ( ) ) ; template < typename > static no test ( ... ) ; public : static constexpr bool value = std :: is_same < decltype ( test < T > ( 0 ) ) ,yes > :: value ; } ; template<typename T> struct has_size_method { private: typedef std::true_type yes; typedef std::false_type no; template<typename U> static auto test(int) -> decltype(std::declval<U>().size() == 1, yes()); template<typename> static no test(...); public: static constexpr bool value = std::is_same<decltype(test<T>(0)),yes>::value; };

The only drawback: it doesn’t work on VC11 (or older).

The Boost way

The upcoming version 1.54 of Boost will contain Boost.TTI library, which offers very similar functionality, but it suffers from the same problem (though with Boost I’m more inclined to say that it’s a design decision) that the first solution does – inherited functions are invisible:

#include <iostream> #include <iomanip> #include <type_traits> #include <sstream> #include <boost/tti/has_member_function.hpp> struct int_size { int size ( ) const { return 42 ; } ; } ; struct derived_int_size : int_size { } ; struct size_t_size { std :: size_t size ( ) const { return 42 ; } } ; struct derived_size_t_size : size_t_size { } ; struct void_size { void size ( ) { } } ; struct no_size { } ; struct template_int_size { template < typename = int > int size ( ) const { return 42 ; } } ; struct overloaded_size { size_t size ( ) const { return 42 ; } int size ( ) { return 41 ; } } ; BOOST_TTI_HAS_MEMBER_FUNCTION ( size ) int main ( ) { using namespace std ; #define dbg(x) { stringstream s; s << boolalpha << left << setw(8) <<\ (has_member_function_size<x,size_t,boost::mpl::vector<>, boost::function_types::const_qualified>::value ||\ has_member_function_size<x,int,boost::mpl::vector<>, boost::function_types::const_qualified>::value)\ << #x; cout << s.str() << endl; } dbg ( int_size ) ; dbg ( derived_int_size ) ; dbg ( size_t_size ) ; dbg ( derived_size_t_size ) ; dbg ( void_size ) ; dbg ( no_size ) ; dbg ( template_int_size ) ; dbg ( overloaded_size ) ; } #include <iostream> #include <iomanip> #include <type_traits> #include <sstream> #include <boost/tti/has_member_function.hpp> struct int_size{ int size() const { return 42; }; }; struct derived_int_size : int_size{}; struct size_t_size{ std::size_t size() const { return 42; } }; struct derived_size_t_size : size_t_size{}; struct void_size{ void size() {} }; struct no_size{}; struct template_int_size{ template<typename=int> int size() const { return 42; } }; struct overloaded_size{ size_t size() const { return 42; } int size() { return 41; } }; BOOST_TTI_HAS_MEMBER_FUNCTION(size) int main() { using namespace std; #define dbg(x) { stringstream s; s << boolalpha << left << setw(8) <<\ (has_member_function_size<x,size_t,boost::mpl::vector<>, boost::function_types::const_qualified>::value ||\ has_member_function_size<x,int,boost::mpl::vector<>, boost::function_types::const_qualified>::value)\ << #x; cout << s.str() << endl; } dbg(int_size); dbg(derived_int_size); dbg(size_t_size); dbg(derived_size_t_size); dbg(void_size); dbg(no_size); dbg(template_int_size); dbg(overloaded_size); }

Output

true int_size false derived_int_size true size_t_size false derived_size_t_size false void_size false no_size true template_int_size true overloaded_size true int_size false derived_int_size true size_t_size false derived_size_t_size false void_size false no_size true template_int_size true overloaded_size

Conclusion

A simple Google search will give plenty of results for this problem, but they are mostly links to the naïve solution (1, 2, 3, 4). The correct way to do it is either to roll your own type trait using Expression SFINAE or use Boost’s solution.

For a description of overload resolution mechanisms, there’s an excellent overview published by ACCU, and if you’re interested in checking how far you can go with it, the fantastic write-up on Flaming Dangerzone is a must read.