Reputation: 933
I used SFINAE many times successfully. To detect if a class provides a function is not a problem. My current problem seems to be the opposite of his problem! Instead of also detect the derived methods, I would prefer to detect only the class' methods. It seems to be related to the fact that the method is a template.
Is it possible to detect a class template method? I tried to instantiate the template with a type which shouldn't harm, but no luck.
struct A { template<class T> void Func( T ) {}; };
struct B :A {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class U, void (U::*)( int ) > struct Sfinae;
template< class T2 > static YesType Test( Sfinae<T2,&T2::Func>* );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
// gives "1"
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
// doesn't compile!
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
return 0;
}
Error message:
error: ‘&A::Func’ is not a valid template argument for type ‘void (B::*)(int)’ because it is of type ‘void (A::*)(int)’
Note that this SFINAE works very well with template methods, just not with derivation! Bad is that it doesn't just detect wrong, it fails compilation.
How to write a SFINAE test without using a 'sample' type (here: the int)?
Edit: Sorry, C++03 only! And LLVM was fine with it, also VS2008, just not GCC and QNX (version I would have to look tomorrow).
Edit2: Didn't know about Coliru! Very cool, here is the error!
Upvotes: 2
Views: 1155
Reputation: 181
updated to C++20 (using concepts) gives us this neat constexpr solution
#include <iostream>
struct A { template<class T> void Func( T ) {}; };
struct B : A {};
struct C {};
template <typename T>
concept CheckForFunc = requires(T t) { t.Func(T()); };
int main(int argc, char* argv[])
{
if constexpr(CheckForFunc<A>) std::cout << "Value A" << std::endl;
if constexpr(CheckForFunc<B>) std::cout << "Value B" << std::endl;
if constexpr(CheckForFunc<C>) std::cout << "Value C" << std::endl;
return 0;
}
the output would be
Value A
Value B
Upvotes: 1
Reputation: 39131
If you know the exact template parameters the member function template should have, this works: (Note it uses some C++11 features that could be replaced by C++03 features)
#include <iostream>
#include <type_traits>
struct A { template<class T> void Func( T ) {} };
struct B :A {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class T2 > static YesType Test(
typename std::enable_if<
std::is_same<void (T::*)(int), decltype(&T2::template Func<int>)>{},
int
>::type );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
// gives "1"
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
// doesn't compile!
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
return 0;
}
Output:
Value A=1
Value B=0
Upvotes: 0
Reputation: 361612
I would use this fix:
Sfinae<T2, decltype(std::declval<T2>().Func(0))>
That is, use the type of the expression obj.Func(0)
and pass it to Sfinae
class template.
Here is the complete code with the fix:
#include <iostream>
#include <utility>
struct A { template<class T> void Func( T ) {}; };
struct B : A {};
struct C {};
template< class T >
struct CheckForFunc
{
typedef char(&YesType)[1];
typedef char(&NoType)[2];
template< class, class > struct Sfinae;
template< class T2 > static YesType Test( Sfinae<T2, decltype(std::declval<T2>().Func(0))> * );
template< class T2 > static NoType Test( ... );
static const bool value = sizeof(Test<T>(0))==sizeof(YesType);
};
int main(int argc, char* argv[])
{
std::cout << "Value A=" << CheckForFunc< A >::value << std::endl;
std::cout << "Value B=" << CheckForFunc< B >::value << std::endl;
std::cout << "Value C=" << CheckForFunc< C >::value << std::endl;
return 0;
}
Output:
Value A=1
Value B=1
Value C=0
I added class C
to this demo.
Online Demo. :-)
Upvotes: 2
Reputation: 208416
The issue is not related to a class template, which is resolved correctly, but to a weird quirk in the address-of-member expression. In particular, with the types:
struct base { void foo(); };
struct derived : base {};
The expression &derived::foo
is of type void (base::*)()
which might or not be intuitive.
As of a test to detect the presence of a member function template, I don't have an answer. You cannot take the address of a template, but you could probably create a fake inaccessible type and try to call the function with that type. The only way that the class could have a function taking that type would be if the function itself is a template. You might want to use this inside of an unevaluated expression to avoid odr-using the template function with your type.
Upvotes: 3