Why the following SFINAE test does not work?

  • Follow


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

0
Reply eshneto (64) 11/8/2010 5:38:23 PM

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.


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

0
Reply Andy 11/9/2010 1:53:20 PM


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


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

0
Reply Michael 11/11/2010 6:59:46 AM

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.


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

0
Reply Andy 11/11/2010 11:30:18 AM

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


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

0
Reply ISO 11/12/2010 9:00:38 PM

{ 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


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

0
Reply ISO 11/13/2010 1:03:02 PM

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


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

0
Reply ISO 11/15/2010 2:15:50 PM

6 Replies
113 Views

(page loaded in 0.094 seconds)

Similiar Articles:













7/3/2012 1:39:45 PM


Reply: