“Invalid template argument” error in Visual Studio

2020-03-18 00:01发布

问题:

suppose you have the code

template <template<class> class BaseType>
class EST16
    : public BaseType<int>
{
public:
    EST16(double d) 
    {
    }
};

template <class T>
class SCEST
{
    T y;
};
typedef EST16<SCEST> EST16_SC;
class Child
    : public EST16_SC
{
public:
    Child()
        : EST16_SC(1.0)
    {
    }

};



class NotWorkingChild
    : public EST16<SCEST>
{
public:
    NotWorkingChild()
        : EST16<SCEST>(1.0)
    {
    }

};



TEST(TemplateTest, TestInstantiate)
{
    Child child;
    NotWorkingChild notWorkingChild; 
}

Child and NotWorkingChild differ only by the typedef. In GCC both compile, in Visual Studio the constructor of NotWorkingChild produces the following error:

2>..\..\..\src\itenav\test\SCKFErrorStateTest.cpp(43) : error C3200: 'SCEST<T>' : invalid template argument for template parameter 'BaseType', expected a class template
2>        with
2>        [
2>            T=int
2>        ]

Can you explain why this is the case? Is there a better portable solution than the typedef?

Thanks!

回答1:

The error message is because NotWorkingChild derives (indirectly) from SCEST<int>, which makes SCEST inside the scope of NotWorkingChild refer to the class SCEST<int>, instead of the template. MSVC is correct to reject this, and GCC4.5 should reject this too (GCC4.5 has more correct injected class name lookup).

Here is a solution that might work for both compilers

class NotWorkingChild
    : public EST16<SCEST>
{
public:
    NotWorkingChild()
        : EST16< ::SCEST >(1.0)
    {
    }

};

Notice that we use the scope resolution operator and need to put a space before :: (the token <: would otherwise be taken as a digraph).


Breaking News: C++0x will make the above work even if you do EST16<SCEST>. The reason is, that it says that if the injected class name is passed to a template template parameter, the injected class name is treated as a template, instead of as a type. Therefor, for C++0x, GCC would be the compiler doing it correctly.



回答2:

It compiles on VS if you change the initialiser to just refer to the template class name, EST16. I.e.:

class NotWorkingChild : public EST16<SCEST>
{
public:
    NotWorkingChild()
        : EST16(1.0)
    {
    }
};