Why cannot the following SFINAE test detect a template member function?
compiling with GCC i get always false from the following code. I believe this is a compiler bug, but someone may 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 &开发者_如何学Pythongt;::result << '\n';
return( 0 );
}
I can't claim to understand why, but I was able to make your code work by not taking U* and by pulling the declaration of the binder type out:
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 actually simplify this a bit by removing the unsigned parameter from the function and just sticking 0u in the typedef within 'declare'.
Again, I can't explain why this intermediate metafunction is necessary but it was required and the above works in MSVC++ 2010
Andy Venikov's answer over in [comp.lang.c++.moderated] (I'm only taking credit for great google-foo (he he, I cheated)):
http://groups.google.com/group/comp.lang.c++.moderated/msg/93017cf706e08c9e
Like Noah I don't know why. Unlike Noah I didn't find a workable solution, but investigating the thing I managed to crash the MingW g++ 4.4.1 compiler (that is, an Internal Compiler Error). This was simply by inconsistently referring to apply
as template and non-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 );
}
Effect 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> _
He he...
PS: I'd love to post this as a "comment", since it's not an "answer".
This is not an answer to why it doesn't work. However, researching through the web, I've found some examples and eventually got to the following code, which may be even more to the point then what I've been trying.
I was trying to detect an specific member function signature, but the code below goes beyond and detects whether a given call is possible, no matter what is the signature. Hope 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 );
}
精彩评论