For example:
template<typename T>
class TypeCheck
{
struct One { char _base;};
struct Two { char _base[2];};
// SFINAE check functions
template <typename CheckType>
One SFINAE_SwapCheck(typename CheckType::type_t*);
template <typename SwType>
Two SFINAE_SwapCheck(...);
public:
enum {result = sizeof( SFINAE_SwapCheck<T>(0) ) == sizeof(One) };
}; // class TypeCheck
TypeCheck<CMyType>::result will be true if CMyType contain type member
type_t, otherwise false.
But how one can check that type contains function member?
I am trying to use pointer to function member and default parameter for
it, like:
template<typename T>
class TypeCheck
{
struct One { char _base;};
struct Two { char _base[2];};
// SFINAE check functions
template <typename CheckType>
One SFINAE_SwapCheck(void (CheckType::*)() = CheckType::my_function);
template <typename SwType>
Two SFINAE_SwapCheck(...);
public:
enum {result = sizeof( SFINAE_SwapCheck<T>() ) == sizeof(One) };
}; // class TypeCheck
but it looks like SFINAE cannot help us in this case because SFINAE is
not extend for default parameter of function argument. Compiler (VC7.0)
decide to call first function in case of SFINAE_SwapCheck<CMyType>()
even if CMyType doesn’t have CMyType::my_function function member and
default parameter cause an error. As for me it looks like subsituation
failure cause an error but I am not sure, can one explain this situation?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
I will try to clarify what I mean:
template<typename CheckType>
class TypeChecker
{
...
// Template cannot be instanceated for some kind of classes.
}; // class TypeChecker
template <typename CheckType>
class Check
{
struct SFINAE_True {char _base;};
struct SFINAE_False {char _base[2];};
template<typename TestType> static SFINAE_True SFINAE_function(const
TypeChecker< TestType >*);
template<typename TestType> static SFINAE_False SFINAE_function(...);
public:
enum { Result = ( sizeof(SFINAE_function<CheckType>(0)) ==
sizeof(SFINAE_True) )};
}; // class Check
For now compiler decides to call first function with any value of
TestType, even if such TypeChecker<TestType> cannot be instantiated. It
would be better if compiler will use IFINAE principle and use
SFINAE_function(...) instead of SFINAE_function(const TypeChecker<
TestType >*) if TypeChecker< TestType > cannot be instantiated.
[]
> But how one can check that type contains function member?
Here you go:
template<class T>
struct has_foo
{
// foo member function is expected to have type void(T::*)()
template<void(T::*)()> struct wrap;
template<class U>
static char(&test(U*, wrap<&U::foo>* = 0))[1];
static char(&test(...))[2];
enum { value = 1 == sizeof(has_foo::test((T*)0)) };
};
struct A {};
struct B { void foo(); };
typedef int assert[(!has_foo<A>::value) ? 1 : -1];
typedef int assert[(has_foo<B>::value) ? 1 : -1];
You can check for the existence of a 'void swap(T&)' member function
like this:
template <typename Type>
struct has_member_swap
{
private:
template <typename T, void(T::*)(T&)>
struct S;
template <typename T>
static One f(S<T, &T::swap>*);
template <typename T>
static Two f(...);
public:
enum { value = sizeof(f<Type>(0)) == sizeof(One) };
};
struct yes
{
void swap(yes&);
};
struct no
{
};
has_member_swap<yes>::value; // true
has_member_swap<no>::value; // false
I haven't tested it on VC7.0 though...
Try searching clc++m for has_member. There have been a lot of threads
about this in early 2005. You can find there a lot of informations
about what is and what isn't possible.
Also see Boost's has_xxx.
Cheers,
Vladimir Marko
Others have already shown how to achieve what you want. I'll just
comment on why your approach doesn't work.
> template<typename T>
> class TypeCheck
> {
> struct One { char _base;};
> struct Two { char _base[2];};
>
> // SFINAE check functions
> template <typename CheckType>
> One SFINAE_SwapCheck(void (CheckType::*)() = CheckType::my_function);
>
> template <typename SwType>
> Two SFINAE_SwapCheck(...);
> public:
> enum {result = sizeof( SFINAE_SwapCheck<T>() ) == sizeof(One) };
> }; // class TypeCheck
>
> but it looks like SFINAE cannot help us in this case because SFINAE is
> not extend for default parameter of function argument. Compiler (VC7.0)
> decide to call first function in case of SFINAE_SwapCheck<CMyType>()
> even if CMyType doesn't have CMyType::my_function function member and
> default parameter cause an error. As for me it looks like subsituation
> failure cause an error but I am not sure, can one explain this situation?
There are other things wrong with your code (like the fact that the
test
functions should be static), but the basic issueis as follows.
A default argument should only be instantiated if the corresponding
function is actually called. So overload resolution has to happen
before that instantiation occurs. The first part of this overload
resolution is to create the list of viable functions, in this case by
"partially instantiating" the two member templates. The list in this
case will be
One SFINAE_SwapCheck(void (T::*)() = <something unknown>);
Two SFINAE_SwapCheck(...);
Perhaps surprisingly, those two are ambiguous when called with an
empty argument list: Since no arguments are passed, no argument-
parameter matches are compared and the two calls are indistinguishable
as far as overload resolution is concerned (see 13.3.2/2).
You therefore end up with an ambiguity error before even getting
to the point where the default argument is instantiated.
I hope that's somewhat helpful,
Daveed
//
// test.cpp
//
#include <string>
struct one_t { char _one [1]; };
struct two_t { char _two [2]; };
class smart {
template< typename T, typename void ( T::* )( T& ) > struct check;
template< typename T > static one_t concept( check< T, &T::swap >* );
template< typename T > static two_t concept( ... );
template< bool C, typename T1, typename T2 > struct select { typedef
T1 result; };
template< typename T1, typename T2 > struct select< false, T1, T2 >
{ typedef T2 result; };
struct custom {
template< typename T > static void swap( T& Object1, T& Object2 ) {
Object1.swap( Object2 );
}
};
struct common {
template< typename T > static void swap( T& Object1, T& Object2 ) {
T tempObject = Object1;
Object2 = Object1;
Object1 = tempObject;
}
};
public:
template< typename T > static void swap( T& Object1, T& Object2 ) {
typedef typename select< sizeof( concept<T>(0) ) == sizeof( one_t
), custom, common >::result method;
method::swap( Object1, Object2 );
}
};
struct why {
void swap( int& ) {}
};
int main() {
std::string name, fame;
smart::swap( name, fame ); // custom way expected
int lame = 0, dame = 0;
smart::swap( lame, dame ); // common way expected
why game, tame;
smart::swap( game, tame ); // common way expected
return 0;
}
MSVC 2003 behaves as expected, but xlC on AIX does not compile, telling
that:
An expression of type "void (why::*)(why &)" cannot be converted to
"void (why::*)(why &)".
Please, can you tell me who's wrong? I just want to understand the
SFINAE principle and I'm a little bit confused for now. If MSVC behavior
is standart, then it's possible to partially implement concept-like
behavior ( as described in C++0x ), especially concept specialization.
Thanks in advance.
//
// test.cpp
//
#include <string>
return 0;
}
An expression of type "void (why::*)(int &)" cannot be converted to
The second typename is illegal; you're specifying a
value parameter, not a type parameter.
However I have one small comment to your code:
> struct one_t { char _one [1]; };
> struct two_t { char _two [2]; };
> ...
> template< typename T > static one_t concept( check< T, &T::swap >* );
> template< typename T > static two_t concept( ... );
> ...
> typedef typename select< sizeof( concept<T>(0) ) == sizeof(one_t), custom,
common >::result method;
> ...
By definition, when sizeof() is applied to a struct, it doesn't necessarily
return the sum of sizes of all its members (due to packing). You should rather
define the concept templates to return the chars themselves:
template< typename T > static char concept( check< T, &T::swap >* = 0 );
template< typename T > static char (&concept( ... ))[2];
I am wondering if anyone can provide an answer to your question from the
standard's point of view.
A.
14.8.2 (template argument deduction) /2, 2nd bullet says
Nontype arguments must match the types of the corresponding nontype
template parameters, or must be convertible to the types of the
corresponding nontype parameters as specified in temp.arg.nontype,
otherwise type deduction fails.
MSVC 2003 is correct, the non-existence of the conversion results in
deduction failure. The code is well-formed. (Well, except for the core
issue #339.)
Cheers,
Vladimir Marko