tags:

views:

452

answers:

3

Hi guys,
Still fighting with templates. In this example, despite the fact that is copied straight from a book I'm getting the following error message: Error 2 error C2784: 'IsClassT<T>::One IsClassT<T>::test(int C::* )' : could not deduce template argument for 'int C::* ' from 'int'.

This is an example from a book Templates - The Complete Guide. (I work with Visual Studio 2010 RC).

  template<typename T> 
    class IsClassT { 
      private: 
        typedef char One; 
        typedef struct { char a[2]; } Two; 
        template<typename C> static One test(int C::*); 
        template<typename C> static Two test(…); 
      public: 
        enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 }; 
        enum { No = !Yes }; 
    }; 

class MyClass { 
}; 

struct MyStruct { 
}; 

union MyUnion { 
}; 

void myfunc() 
{ 
} 

enum E {e1} e; 

// check by passing type as template argument 
template <typename T> 
void check() 
{ 
    if (IsClassT<T>::Yes) { 
        std::cout << " IsClassT " << std::endl; 
    } 
    else { 
        std::cout << " !IsClassT " << std::endl; 
    } 
} 

// check by passing type as function call argument 
template <typename T> 
void checkT (T) 
{ 
    check<T>(); 
} 

int main() 
{ 
    /*std::cout << "int: "; 
    check<int>(); */

    std::cout << "MyClass: "; 
    check<MyClass>(); 
}

And although I know roughly what's going on in this example I cannot fix this error.
Thanks for help.

+3  A: 

My compiler (MSVC2008TS) likes it if you don't fully qualify the test expression:

enum { Yes = sizeof(test<T>(0)) == 1 }; 

But is this even legal code?

John Dibling
This fixes the Codepad error too, nice: http://codepad.org/1BNxJ93K
Bill
+2  A: 

Shouldn't this line

    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 }; 

be

    enum { Yes = sizeof(IsClassT<T>::template test<T>(0)) == 1 }; 

instead?`

(And because I'm anal, I'd write sizeof(IsClassT<T>::template test<T>(0)) == sizeof(One).)

sbi
I have not found it in the standard, but my guts tell me this is not the case. Note that you need to provide the `template` (or `typename` identifiers) when referring to a template instantiation inside another template because the compiler cannot guarantee what the identifier refers to. The difference is that in this example `IsClassT<T>` is not an instantiation of a generic template but rather *this* template. In fact the whole `IsClassT<T>::` is optional (and if removed the code will compile in VS).
David Rodríguez - dribeas
But that is a particularly interesting issue there... if you try a simpler similar example: `template <typename T> struct test { typedef int int_t; void f() { int_t x; } void g() { test<T>::int_t x; } };`, and you instantiate both `f` and `g`, Comeau will process it with no error, but g++ 4.0 will choke in `g`. Note that the problem is the same, and is the result is inconsistent (either it should accept both `f` and `g` or none of them), and that makes me believe that the parser is being tricked by the full qualification. But this is not an authoritative answer whatsoever.
David Rodríguez - dribeas
@David, the `template` is required in C++03, but it's optional in C++0x which introduced the notion of a "current instantiation" and a "member" of it. If the template is a member of the current instantiation, `::template` is optional if the compiler can lookup the template in that instantiation (if the template is defined in a dependent base class, it may still require `::template`). C++03 did not have such differences and always required it at `14.2/4`. In fact it looks comeau implements the C++0x rules for `typename` (clang does so too, even in C++03 mode).
Johannes Schaub - litb
(C++03 said "The keyword typename shall always be specified when the member is referred to using a qualified name, even if the qualifier is simply the class template name." but of course that's kinda silly given that the compiler can simply lookup at definition time whether or not it's a type). C++0x has the same current-instantiation relaxations for `typename` too :)
Johannes Schaub - litb
@Johannes: Since C++11 is only due in, erm, 2011, I interpret your comment as confirming my suspicion that the `template` is missing. `:)`
sbi
+1  A: 

Not exactly an answer to this question, but rather a different approach to your problem. I find it easier to do SFINAE tests in specializations rather than in functions defined in that template:

// used to pass a type tested with SFINAE
template<typename T> struct tovoid { typedef void type; };

Using it to pass a type that may be invalid:

template<typename T, typename U = void> 
struct is_class {
  static bool const value = false;
};

// if "int T::*" is a valid type, this specialization is used
template<typename T>
struct is_class<T, typename tovoid<int T::*>::type> {
  static bool const value = true;
};

This way it's considerably shorter and the noise with sizeof and things aren't done.

Johannes Schaub - litb