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

Why the following SFINAE test does not work?

108 views
Skip to first unread message

Elias Salomão Helou Neto

unread,
Nov 8, 2010, 6:38:23 PM11/8/10
to
{ The question in the subject line is, "Why the following SFINAE test does not
work?". -mod/aps }

template< class T >
class has_apply {

typedef char yes[1];
typedef char no[2];

template< class U, U u >
struct coerce {};

template< class U, unsigned n >
static yes& test( U*, coerce< 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, 0
>( (T*)(0) ) ) );
};

When compiled with gcc4.5 the test results always false.

Elias.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Andy Venikov

unread,
Nov 9, 2010, 2:53:20 PM11/9/10
to
On 11/8/2010 6:38 PM, Elias Salomão Helou Neto wrote:

> template< class T>
> class has_apply {
>
> typedef char yes[1];
> typedef char no[2];
>
> template< class U, U u>
> struct coerce {};
>
> template< class U, unsigned n>
> static yes& test( U*, coerce< 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, 0
>> ( (T*)(0) ) ) );
> };
>
> When compiled with gcc4.5 the test results always false.
>
> Elias.
>

The problem in your case is that when you're trying to instantiate coerce<...>,
you're trying to use the result of operator& in a constant-expression context,
and that is prohibited.

This is a well-know problem and it has several solutions.

I.e:
http://www.boost.org/doc/libs/1_41_0/doc/html/proto/appendices.html#boost_proto.appendices.implementation.function_arity

(It detects if a construct can be called with given arguments).

In your case, since you want "apply" to be a template, the following approach
may be better:

http://www.rsdn.ru/forum/cpp/2720363.aspx
(The page is in Russian, but just look at the code - it's simple and the
solution is beautiful).

Relevant discussions:
https://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/2018e65c1959744f/28fa54f793e41408?hl=en%1Cfa54f793e41408

http://article.gmane.org/gmane.comp.lib.boost.devel/197923/


HTH,
Andy.

Michael Doubez

unread,
Nov 11, 2010, 7:59:46 AM11/11/10
to

On Nov 9, 8:53 pm, Andy Venikov <swojchelo...@gmail.com> wrote:

> On 11/8/2010 6:38 PM, Elias Salom�o Helou Neto wrote:
>
>
>
> > template< class T>
> > class has_apply {
>
> > typedef char yes[1];
> > typedef char no[2];
>
> > template< class U, U u>
> > struct coerce {};
>
> > template< class U, unsigned n>
> > static yes& test( U*, coerce< 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, 0
> >> ( (T*)(0) ) ) );
> > };
>
> > When compiled with gcc4.5 the test results always false.
>
> > Elias.
>
> The problem in your case is that when you're trying to instantiate
coerce<...>,
> you're trying to use the result of operator& in a constant-expression
context,
> and that is prohibited.

I don't understand your answer. I have read the thread you mention but
in this case, the program is not ill formed. It is just that the
relevant overloads are not matched.

As an example, the following code, based on the same mechanism as the
OP's, works:

#include <iostream>

typedef char (&no_tag)[1];
typedef char (&yes_tag)[2];

template <typename T, void (T::*)(const double&)> struct ptmf_helper
{};

template<typename T> no_tag has_apply_helper(...);

template<typename T>
yes_tag has_apply_helper(ptmf_helper<T, &T::template apply<0u> >* p);

template<typename T> struct has_apply {
static const bool value = sizeof(has_apply_helper<T>(0)) ==
sizeof(yes_tag);
};

struct A {
template<unsigned n>
void apply( const double& ){}
};

class B {
};

int main()
{
std::cout << std::boolalpha << has_apply< A >::value << '\n';
std::cout << std::boolalpha << has_apply< B >::value << '\n';
return( 0 );

}

Produce:
true
false

But the OP's code doesn't work as expected.

[snip: other solutions to has_apply<> ]

--
Michael

Andy Venikov

unread,
Nov 11, 2010, 12:30:18 PM11/11/10
to

On 11/11/2010 7:59 AM, Michael Doubez wrote:
<snip>

>> The problem in your case is that when you're trying to instantiate
> coerce<...>,
>> you're trying to use the result of operator& in a constant-expression
> context,
>> and that is prohibited.
>
> I don't understand your answer. I have read the thread you mention but
> in this case, the program is not ill formed. It is just that the
> relevant overloads are not matched.

You're right, I think I jumped the gun. When I was playing around with
OP's program, at some point I got a compile error about using operator&
in a constexpr context. Of course by that time I've modified the
original code so that an address of a variable was being taken and
forgot about that. Using an address of a member should and is allowed to
be used as a non-type template argument. My bad.

Now, back to the original problem. Here's a piece of code that pinpoints
the issue:

struct with_apply
{
template <unsigned>
void apply(const double&);
};

auto p = &with_apply::apply<0>;

This produces an error that says that the compiler couldn't chose the
right apply among the overloads. This, however, works:

typedef void (with_apply::* ptype)(const double&);
auto p = (ptype)&with_apply::apply<0>;

So, even if we specify the template parameter explicitly, the compiler
still wants us to identify the overload by specifying the types of
function paramters. (const double&)
Frankly, I'm not sure whether it's a compiler bug or standards-complying
behavior.

But, since OP is already using the exact type of the mem-function
pointer on the same line, he can make his code work by changing his
"yes" test function to the following:

template <typename U, unsigned n>


static yes& test ( U*, coerce<void (U::*)(const double&),

(void (U::*)(const double&)&U::template apply<n> >* = 0);

<snip>

HTH,
Andy.

Daniel Krügler

unread,
Nov 12, 2010, 10:00:38 PM11/12/10
to
Am 11.11.2010 18:30, schrieb Andy Venikov:
>
> On 11/11/2010 7:59 AM, Michael Doubez wrote:
> <snip>
>
>> I don't understand your answer. I have read the thread you mention but
>> in this case, the program is not ill formed. It is just that the
>> relevant overloads are not matched.
>
> You're right, I think I jumped the gun. When I was playing around with
> OP's program, at some point I got a compile error about using operator&
> in a constexpr context. Of course by that time I've modified the
> original code so that an address of a variable was being taken and
> forgot about that. Using an address of a member should and is allowed to
> be used as a non-type template argument. My bad.

It could even be an address of a variable of static storage duration
or any other constant expression (in C++0x), e.g. the following example in
namespace scope should also be well-formed:

template<class U, U u>
struct coerce {};

int i;

template<class U, int* P>
int test(U*, coerce<U*, P>* = 0);

template<class U, int* P>
void test(...);

typedef decltype(test<int, &i>(0)) type;

static_assert(sizeof(type) > 0, "Ouch");

> Now, back to the original problem. Here's a piece of code that pinpoints
> the issue:
>
> struct with_apply
> {
> template <unsigned>
> void apply(const double&);
> };
>
> auto p = &with_apply::apply<0>;

This should be well-formed, and the OP's example should be so as well.

> This produces an error that says that the compiler couldn't chose the
> right apply among the overloads.

IMO this is a compiler-defect.

HTH & Greetings from Bremen,

Daniel Kr�gler

Elias Salomão Helou Neto

unread,
Nov 13, 2010, 2:03:02 PM11/13/10
to
{ Attributions added for quotes; line breaks fixed; "..." ellipses removed from
URL; URL tested; short URL added. Elias, and others, please take some care with
the *form* of your contentwise very good articles -- so that they communicate
even better. TIA., -mod/aps }

* Daniel Krügler, on 13.11.2010 04:00:


> * Am 11.11.2010 18:30, schrieb Andy Venikov:
>
> This should be well-formed, and the OP's example should be so as well.
>
> > This produces an error that says that the compiler couldn't chose the
> > right apply among the overloads.
>
> IMO this is a compiler-defect.

Could you please indicate the relevant standard sections? I am
planning to file a bug report.

I've eventually implemented some of the ideas provided by Andy's links
(thank you). The OP's solution, whether compliant or not was so much
easier than the lenghtier one I've obtained, which can be seen in a well-
formatted manner at

http://stackoverflow.com/questions/4136359/why-cannot-the-following-sfinae-test-detect-a-template-member-function/4173368#4173368

{ short url <url: http://preview.tinyurl.com/3ysrrnw>. -mod }

It is worth to remark that the test in the link above is a different
one from the OP's code. The test implemented in the link returns true
whenever some call is possible for a member function, while the OP's should
result true if an exact signature is match. So th OP's test should still be
useful in some situations.

Elias

Daniel Krügler

unread,
Nov 15, 2010, 3:15:50 PM11/15/10
to
On 13.11.2010 20:03, Elias Salomão Helou Neto wrote:
> { Attributions added for quotes; line breaks fixed; "..." ellipses removed from
> URL; URL tested; short URL added. Elias, and others, please take some care with
> the *form* of your contentwise very good articles -- so that they communicate
> even better. TIA., -mod/aps }
>
> * Daniel Krügler, on 13.11.2010 04:00:
>> * Am 11.11.2010 18:30, schrieb Andy Venikov:
>>
>> This should be well-formed, and the OP's example should be so as well.
>>
>>> This produces an error that says that the compiler couldn't chose the
>>> right apply among the overloads.
>>
>> IMO this is a compiler-defect.
>
> Could you please indicate the relevant standard sections? I am
> planning to file a bug report.

14.8.1 + 14.8.2. I don't see anything that conflicts with the required
behaviour. All template arguments are explicitly provided and the corresponding
type constraints are satisfied. I don't see any invalid construct.

Further evidence that this is a compiler-bug is the fact, that if you
move all of

typedef char yes[1];
typedef char no[2];

template< class U, U u >
struct coerce {};

template< class U, unsigned n >


static yes& test( U*, coerce< void (U::*) ( const double& ) ,

&U::template apply< n > >* = 0 );


template< class U, unsigned n >
static no& test( ... );

in a non-template class X and replace the test

test<T, 0>(0)

within has_apply by

X::test<T, 0>(0)

the example works as expected.

HTH & Greetings from Bremen,

Daniel Krügler

0 new messages