0

I'm trying to pass the type of a class member as a template argument. For this I'm using the following code:

class C
{
public:
    int a;
};
class B
{
public:
    template<typename _T> void Test() {}
    // Specialize for int for Test testing purposes.
    template<> void Test<int>() { printf("Success!\n"); }
};

//Later:
B b;
C c;
b.Test<decltype(c.a)>();

This gives me the following error:

error C2662: 'void B::Test(void)' : cannot convert 'this' pointer from 'C' to 'B &' Reason: cannot convert from 'C' to 'B' Conversion requires a second user-defined-conversion operator or constructor

However, it does work if I use the following code:

decltype(c.a) d;
b.Test<decltype(d)>();

Why doesn't it work if I simply use the class member directly? I'm using Visual Studio 2012.

ThaPear
  • 3
  • 1
  • I don't know if this is the issue, but Visual Studio's support for C++11 is patchy. VS2013 is better. Try the code on oneide.com – Neil Kirk Aug 11 '14 at 17:15
  • 1
    `decltype` in VS2012 is buggy as hell. However, this has been fixed in VS2013. – 101010 Aug 11 '14 at 17:30
  • It was indeed a problem in Visual Studio 2012, Visual Studio 2013 compiles it without issues. The template specialization was only in there to show that it worked with the second version. It was not part of the initial erroneous code. – ThaPear Aug 12 '14 at 11:12

2 Answers2

1

I don't think it is legal to define a specialization inside the class, it has to be at namespace level (g++ does not compile your code, error: explicit specialization in non-namespace scope 'class B'). Try to define the template outside the class,

template<> void B::Test<int>() { printf("Success!\n"); }

This code works perfectly fine for me (on g++), if it doesn't work for you it is probably a VS issue.

#include <iostream>

using namespace std;

class C
{
public:
    int a;
};

class B
{
public:
    template<typename _T> 
    void Test() {}
    // Specialize for int for Test testing purposes.
};

template<> void B::Test<int>() { cout << "Success!\n"; }

int main()
{
    B b;
    C c;
    b.Test<decltype(c.a)>();
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • This code compiles fine in VS2013, however as you've already figured out according to *§14.7.3/2 Explicit specialization [temp.expl.spec]* this is not a standard compliant behaviour but rather a VS extension. – 101010 Aug 11 '14 at 18:02
1

7.1.6.2(4)

For an expression e, the type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e.
  • If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  • otherwise, decltype(e) is the type of e.

VS2012 seems to forget (or was never told) the part which I have bolded, the case where e is an unparenthesized class member access. Therefore it could be falling back to the rule for when e is an lvalue, so for VS2012 decltype(c.a) denotes int& (a reference to int).

Your example also contains an identifier beginning with _ and a capital letter (_T) which is undefined behaviour. See What are the rules about using an underscore in a C++ identifier?

Community
  • 1
  • 1
Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • Thank you for the clear answer. However, I'm confused: How is the identifier _T an issue? Could you elaborate? When I look in for example I see things like _Ty all over. I fact, most of the types used there start with a `_` and a capital letter. – ThaPear Aug 12 '14 at 12:19
  • @ThaPear That's exactly the reason. `` is part of the standard library, which is the only place where it is allowed. See [What are the rules about using an underscore in a C++ identifier?](http://stackoverflow.com/q/228783/1639256) – Oktalist Aug 12 '14 at 13:14