megabyte1024
megabyte1024

Reputation: 8660

How to instantiate, by the standard, a class template containing nested subclass templates

There is a template declaration

template<class C> struct Data {
    template<typename T> struct Item1 {
        void Test() {
        }
    };
};

Using MSVC v19.28 with the compile option /std:c++latest, I tried to instantiate the template as follows:

template<class C> struct Data {
    template<typename T> struct Item1 {
        void Test() {
        }
    };
};

template <class C> struct MyData : Data<C> {
    MyData::Item1<int> item10;              // (1)
};

struct S;

MyData<S> dataS;

The code was compiled successfully with MSVC (see the link), but gcc 11.1 and clang 12.0.0 could not compile it (links for gcc and clang)

I tried to instantiate the template using different declarations

template <class C> struct MyData : Data<C> {
    MyData::Item1<int> item10;              // (1)
    Data<C>::Item1<int> item11;             // (2)
    Data<C>::template Item1<int> item12;    // (3)
};

MSVC compiles all of them, but gcc and clang do not.

I have "found" a solution, which "works" with all the mentioned compilers

template<typename C, typename T> struct Item2 {
    void Test() {
    }
};

template<class C> struct Data {
};

template <class C> struct MyData : Data<C> {
    Item2<C, int> item20;
};

struct S;

MyData<S> dataS;

It uses a stand-alone template.

How to declare the original template instantiation to conform to the C ++ standard? The fact that MSVC compiles all 3 options, is a flaw of it?

Upvotes: 1

Views: 153

Answers (1)

user4442671
user4442671

Reputation:

There's a few things going wrong here:

template <class C> struct MyData : Data<C> {
  MyData::Item1<int> item10;
};

MyData is an incomplete type here, so we can't use it just yet in that way. However, since Item1 is inherited from Data<C>, we should be able to just refer to it directly:

Data<C>::Item1<int> item10;

But now, Data<C>::Item1 is a dependant type, so we need to use typename to tell the compiler that it is, in fact, a type:

typename Data<C>::Item1<int> item10;

Finally, because Item1 is a template member of a dependant type, we also need to tell the compiler that it is a template (as opposed to a constant or something).

typename Data<C>::Item1 template<int> item10;

So at the end, we land at:

template<class C> struct Data {
    template<typename T> struct Item1 {
        void Test() {
        }
    };
};

template <class C> 
struct MyData : Data<C> {
    typename Data<C>::template Item1<int> item10;
};

struct S;

MyData<S> dataS;

Upvotes: 4

Related Questions