Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SFINAE limitations

173 views
Skip to first unread message

Alexander Kozlov

unread,
Jan 6, 2006, 7:40:54 AM1/6/06
to
Hello,
SFINAE can help us to check that template type parameter contain
type member with defined name.

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! ]

Vladimir Frolov

unread,
Jan 6, 2006, 8:35:35 PM1/6/06
to
It would be perfect if we will have not only SFINAE principle but also
instantiation-failure-is-not-an-error (IFINAE) one. (I mean if compiler
cannot instantiate template argument of template function than it has
to look for other overloaded function).
So that, we will able to check anything and change behavior in depend
of what members (any function, type, or data) exist in template
argument and check accessibility of this member.

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.

Maxim Yegorushkin

unread,
Jan 6, 2006, 8:41:03 PM1/6/06
to

Alexander Kozlov wrote:

[]

> 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];

Fuz

unread,
Jan 6, 2006, 8:39:55 PM1/6/06
to
Alexander Kozlov wrote:
> But how one can check that type contains function member?

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...

Vladimir Marko

unread,
Jan 7, 2006, 7:25:14 AM1/7/06
to
Alexander Kozlov wrote:
> Hello,
> SFINAE can help us to check that template type parameter contain
> type member with defined name.
>
> For example:
[...]

> 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:
>
[...]

>
> but it looks like SFINAE cannot help us in this case because SFINAE is
> not extend for default parameter of function argument. [...]

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

Daveed Vandevoorde

unread,
Jan 9, 2006, 6:22:16 PM1/9/06
to
Alexander Kozlov wrote:
[...]

> 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:

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

Alexander Kozlov

unread,
Jan 11, 2006, 6:23:14 AM1/11/06
to
Thank you for your answers. I've got another question to be clarify.
Here is the code:

//
// 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.

Alexander Kozlov

unread,
Jan 11, 2006, 10:26:31 AM1/11/06
to
Sorry, previous post was incorrect. This one is ok.

//
// test.cpp
//

#include <string>

return 0;
}

An expression of type "void (why::*)(int &)" cannot be converted to

Hyman Rosen

unread,
Jan 12, 2006, 7:31:41 AM1/12/06
to
Alexander Kozlov wrote:
> template< typename T, typename void ( T::* )( T& ) > struct check;

The second typename is illegal; you're specifying a
value parameter, not a type parameter.

Aleš Pergl

unread,
Jan 12, 2006, 7:38:52 AM1/12/06
to
Comeau online accepts this code (when you remove the redundant 'typename'
keyword from the struct check definition) so even though I can't back it
up with analysis of the standard, I would think that MSVC is the one who
has it right this time.

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.

Vladimir Marko

unread,
Jan 12, 2006, 7:35:42 AM1/12/06
to
Alexander Kozlov wrote:
[...]
[...]

> 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::*)(int &)" 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.

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

0 new messages