Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested class of class template can be "incomplete"

I'm at a loss as to how to explain why it is valid to create the member inner in the class template OuterTempl<T> whereas it is illegal to do so in the untemplated class Outer.

// Non-template version
struct Outer
{
    struct Inner;
    Inner inner;   // incomplete type (I get this)
};

struct Outer::Inner
{
};

// Template version
template<typename T>
struct OuterTempl
{
    struct InnerTempl;
    InnerTempl inner; // OK ... Huh!?
};

template<typename T>
struct OuterTempl<T>::InnerTempl
{
};

int main()
{
}

See also ideone.

like image 902
Olumide Avatar asked Feb 11 '23 05:02

Olumide


2 Answers

Yes - consider [temp.mem.class]/1:

A member class of a class template may be defined outside the class template definition in which it is declared.
[ Note: The member class must be defined before its first use that requires an instantiation (14.7.1). For example,

template<class T> struct A {
    class B;
};

A<int>::B* b1;  // OK: requires A to be defined but not A::B

template<class T> class A<T>::B { };

A<int>::B b2;   // OK: requires A::B to be defined

— end note ]

It is also important to mention that the definition of inner, which constitutes the use of Inner the above note describes, is only instantiated once it is required:

Unless a member […] has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist;

Since no instantiation of OuterTempl is present in your code, the definition of inner is never instantiated, and an instantiation of Inner is never necessitated. The completeness of the nested class type for such a declaration is thus only required at the point of instantiation. You don't instantiate OuterTempl here, but if you would do that before the definition of Inner, the code would be ill-formed.

That is,

template<typename T>
struct OuterTempl
{
    struct InnerTempl;
    InnerTempl inner;
};

template struct OuterTempl<int>; // Bad - Ill-formed (NDR?)

template<typename T>
struct OuterTempl<T>::InnerTempl {};

template struct OuterTempl<int>; // Fine

Demo.

like image 74
Columbo Avatar answered Feb 20 '23 15:02

Columbo


The member types need to be defined when the class is defined. However, a class template doesn't get defined until it gets instantiated. Any kind of dependent types can change till then. The definition of members is only needed when the class gets instantiated.

On the other hand the definition of a non-template class is a definition which needs to know the size of its member at that point.

like image 32
Dietmar Kühl Avatar answered Feb 20 '23 15:02

Dietmar Kühl