3

In response to .. some other question somewhere, I wrote this code.

struct no_type{};
template<typename T> struct has_apply {
    static decltype(T().apply<0u>(double())) func( T* ptr );
    static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}

Now it seems to me that result should be true if T offers a non-static member function "apply" that accepts a double rvalue and a template parameter literal, and false otherwise. However, the example given actually fails to compile for class B, when compiling has_apply<B>. Shouldn't the fact that the substitution of T failed in the decltype statement mean that it simply calls the other func? Isn't that kind of the point of SFINAE?

Solved in the most ridiculous, pointless fashion ever:

struct no_type{};
template<typename T> struct has_apply {
    template<typename U> static decltype(U().apply<0u>(double())) func( U* );
    template<typename U> static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}
usta
  • 6,699
  • 3
  • 22
  • 39
Puppy
  • 144,682
  • 38
  • 256
  • 465

1 Answers1

3

SFINAE applies when substitution fails for a function template's template parameter, not for a class template's template parameter that has the (non-template) function in question as a member, as is in your case.

After fixing that, you should at least change decltype(T().apply<0u>(double())) to decltype(T().template apply<0u>(double())) because T() expression is of a dependent type. The reason for that is this: when the compiler first sees T().apply<0u>, it doesn't know anything about T yet, so how should it parse the tokens apply and < after .? apply might be a member template, and then < would start the argument list for it. OTOH apply might instead be a non-template member (e.g. a data member), and then < would be parsed as 'less-than' operator. There is an ambiguity, and it's still too early for the compiler to resolve that at this point. There is a need for a disambiguation mechanism a programmer could use to tell the compiler what apply is expected to be: a template or not. And here comes the .template (or ->template, or ::template) construct to the rescue: if it's present, the compiler knows it should be a template member, otherwise if it's not present then the compiler knows the member shouldn't be a template.

Finally here's an example I created that works correctly and produces the desired results on g++ 4.5.0 with -std=c++0x:

#include <iostream>

template < class T >
decltype( T().template apply< 0u >( double() ) ) f( T &t )
{
    return t.template apply< 0u >( 5. );
}

const char *f( ... )
{
    return "no apply<>";
}

class A {
public:
    template < unsigned >
    int apply( double d )
    {
        return d + 10.;
    }
};

class B {};

int main()
{
    A a;
    std::cout << f( a ) << std::endl;
    B b;
    std::cout << f( b ) << std::endl;
}

Output is:

15
no apply<>

Now if you remove both .template from the first f() definition, then the output becomes:

no apply<>
no apply<>

Which is to indicate substitution failure for class A as it doesn't have any non-template member named apply. SFINAE in action!

usta
  • 6,699
  • 3
  • 22
  • 39
  • You should also use declval() instead of T() in case T doesn't have a default constructor. VS2010 doesn't have it but it can be made: http://stackoverflow.com/questions/2638843/should-i-use-c0x-features-now – Edward Strange Nov 09 '10 at 18:45
  • @Noah Roberts: That's right! That is part of why I said 'at least' above ;) – usta Nov 09 '10 at 18:49
  • @usta: I still don't get it. T is not a dependent type, it is the template type. If you make a member variable of type T, you don't say `typename T t;`, which you do for dependent types. – Puppy Nov 09 '10 at 18:54
  • @DeadMG: Please note that the need to add `template` after `.` is not the biggest issue here. The fact that SFINAE concerns itself only with function templates and their parameters is the main thing. Anyway, an object of type `T` (such as `T()`) is an object of dependent type (a type that depends on a template parameter), so one needs to say `.template` to access a member template of that object. – usta Nov 09 '10 at 19:14
  • @DeadMG: Please see if my edited answer is clearer on that point. – usta Nov 09 '10 at 19:29
  • @usta: Visual Studio won't even accept that syntax. I see what you mean about the parsing issues, but it won't take it. – Puppy Nov 09 '10 at 19:40
  • @DeadMG: That's a bug then. GCC 4.5 correctly accepts it (-std=c++0x). And it doesn't accept without `.template` (well in the example I created it does compile because of SFINAE, but works differently at runtime :) ). I'm going to post my example shortly. – usta Nov 09 '10 at 20:02
  • @usta: Visual Studio gives EOF error on that snippet - but if I remove the "template" keyword, it compiles fine and gives `15, no apply<>` as the output. – Puppy Nov 09 '10 at 21:06
  • @DeadMG: Thanks for the info. I think it would be good to report this to MS; I'll see if I can find the nerve to do so... – usta Nov 09 '10 at 21:16
  • @usta: While I believe that the fact that it won't compile with the Standard correct syntax is ungreat, the fact that it will compile with the easier syntax is probably a good thing. – Puppy Nov 09 '10 at 21:26
  • @DeadMG: I can't agree with that, the easier syntax is for non-templates only. Imagine we have another class in the mix: `class C { public: int apply; };`, and in `main()` we have `C c; cout << f( c );`, what should it print in this case? If we have `.template`, the standard says it shall print "no apply<>", GCC would agree, and so should vc++ but it doesn't now. If we don't have `.template`, the standard says it shall print "0" (regardless of `c.apply`'s value or even if it is initialized or not). Why? That's a good question to be left as an exercise for the reader ;) – usta Nov 09 '10 at 22:08
  • @usta: Don't get me started on how incredibly badly designed C++'s grammar is. – Puppy Nov 10 '10 at 10:29
  • @DeadMG: I think we shall agree to disagree on that matter, then. – usta Nov 10 '10 at 11:51