Why doesn't the next SFINAE test detect a member function of a template?

compiling with GCC I always get false from the following code. I believe this is a compiler error, but someone might know better.

#include <iostream> template< class T > class has_apply { typedef char yes[1]; typedef char no[2]; template< class U, U u > struct binder {}; template< class U, unsigned n > static yes& test( U*, binder< void (U::*) ( const double& ), &U::template apply< n > >* = 0 ); template< class U, unsigned n > static no& test( ... ); public: static const bool result = ( sizeof( yes ) == sizeof( test< T, 0u >( (T*)(0) ) ) ); }; class A { public: template< unsigned n > void apply( const double& ); }; int main() { std::cout << std::boolalpha << has_apply< A >::result << '\n'; return( 0 ); } 
+4
source share
4 answers

I can’t say to understand why, but I was able to get your code to work without accepting U * and pulling out a binder type declaration:

 template< class T > class has_apply { public: typedef char yes[1]; typedef char no[2]; template< class U, U u > struct binder {}; typedef binder< void (T::*)(const double&), &T::template apply<0u> > b; template < typename V, unsigned n > struct declare { typedef binder< void (V::*)(const double&), &V::template apply<n> > type; }; template< typename U, unsigned n > static yes& test( typename declare<U,n>::type * ); template< class U, unsigned n > static no& test( ... ); static const bool result = ( sizeof( yes ) == sizeof( test< T, 0u >( 0 ) ) ); }; 

You can simplify this a bit by removing the unknown parameter from the function and simply pasting 0u in the typedef in 'declare'.

Again, I can’t explain why this intermediate metaphor is necessary, but it was necessary, and it works in MSVC ++ 2010

+1
source

Andy Venikov answers in [comp.lang.C ++. moderated] (I only take credit for a great google-foo (he is it, I cheated)):

http://groups.google.com/group/comp.lang.c++.moderated/msg/93017cf706e08c9e

+1
source

Like Noah, I don’t know why. Unlike Noah, I did not find a suitable solution, but I learned that I was able to compromise the MingW g ++ 4.4.1 compiler (that is, an internal compiler error ). This was simply not a consistent reference to apply as a template and not a template:

 #include <iostream> template< class T > class has_apply { template< class U, U u > struct binder {}; template< class U > static double test( U*, binder< void (U::*) ( const double& ), //&U::template apply< 0 > &U::apply >* = 0 ); public: static binder< void (T::*) ( const double& ), &T::template apply< 0 > >* dummy(); static const bool result = sizeof( test( (T*)(0), dummy() ) ); }; class A { public: // template< unsigned n > void apply( const double& ); }; int main() { std::cout << std::boolalpha << has_apply< A >::result << '\n'; return( 0 ); } 

Impact on g ++:

  C: \ test> g ++ -std = c ++ 98 y.cpp
 y.cpp: In instantiation of 'has_apply':
 y.cpp: 38: instantiated from here
 y.cpp: 24: internal compiler error: in instantiate_type, at cp / class.c: 6303
 Please submit a full bug report,
 with preprocessed source if appropriate.
 See for instructions.

 C: \ test> _

Is he...

PS: I would like to post this as a “comment”, since this is not a “response”.

0
source

This is not an answer to the question of why this does not work. However, researching over the Internet, I found a few examples and ended up with the following code, which may be even more accurate than what I tried.

I tried to detect a specific signature of a member function, but the code below goes beyond and determines whether a given call is possible, regardless of what the signature is. We hope that the comments will be helpful.

 #include <iostream> template< class T > class has_apply { class yes { char c; }; class no { yes c[2]; }; struct mixin { void apply( void ); }; // Calling derived::apply is only non-ambiguous if // T::apply does not exist, cf. 10.2.2. template< class U> struct derived : public U, public mixin {}; // The following template will help on deduction based on this fact. // If U is type void (mixin::*) (void) then the template can be // instantiated with u = &derived< U >::apply if and only if T::apply // does not exist. template< class U, U u > class binder {}; // Therefore, the following template function is only selected if there // is no T::apply: template< class U > static no deduce( U, binder< void (mixin::*) (void), &derived< U >::apply >* = 0 ); // Selected otherwise. static yes deduce( ... ); // Provides an T object: static T T_obj( void ); public: static const bool result = ( sizeof( yes ) == sizeof( deduce( T_obj() ) ) ); }; namespace aux { // Class to represent the void type as a "true" type. class void_type {}; // deduce() some lines below will give us the right answer based on // the return type of T::apply<>, but if it is void we cannot use a // call to T::apply as an argument to deduce. In fact, the only // function in c++ that can take such an argument is operator,() with // its default behaviour and if an overload is not well formed it // falls back to default. template< class T > T& operator,( const T&, void_type ) {}; // Copies the constness of T into U. This will be required in order // to not get false positives when no const member is defined. template< class T, class U > struct copy_constness { typedef U result; }; template< class T, class U > struct copy_constness< const T, U > { typedef const U result; }; } template< class T > class has_correct_apply{ class yes { char c; }; class no { yes c[2]; }; // We assume has_apply< T >::result is true so the following class // is well declared. It is declared in a way such that a call to // derived::apply< n >() is always possible. This will be necessary // later. struct derived : public T { using T::apply; // possible iff has_apply< T >::result == true // This template function will be selected if the function call // we wish is otherwise invalid. template< unsigned n > static no apply( ... ); }; // const_correct_derived will have the same constness than T. typedef typename aux::copy_constness< T, derived >::result const_correct_derived; // Provides a const correct derived object. static const_correct_derived derived_obj( void ); // Only possible call was derived::apply: call is impossible for signature: static no deduce( no ); // Since te returned value of it will most likely be // ignored in our code (void must be always [almost, see next] // ignored anyway), we return yes from this: static yes deduce( ... ); // As we noticed, an overload of operator,() may make an exact match necessary. // If we want this we could simply have used "no" instead of "yes" above and: // static no deduce( aux::void_type ); public: static const bool result = ( sizeof( yes ) == sizeof( deduce( ( derived_obj().template apply< 0u >( 0.0 ), aux::void_type() ) ) ) ); // Note: Inteestingly enough, GCC does not detect an private subclass default // constructor and so const_correct_derived() could be used instead of // having a function derived_obj(), but I do not know if this behavoiur is // standard or not. }; struct C { template< unsigned n > int apply( double, unsigned m = 10 ) const; private: C(); }; struct D { template< unsigned n > int apply( const double& ); private: D(); }; struct E : public C { }; struct Without{}; #include "mp.h" int main() { std::cout << has_apply< E >::result << '\n'; std::cout << has_correct_apply< const E >::result << '\n'; std::cout << has_correct_apply< const D >::result << '\n'; std::cout << has_correct_apply< D >::result << '\n'; // E e; return( 0 ); } 
0
source

All Articles