Pilar Latiesa
Pilar Latiesa

Reputation: 695

Class template inheriting from incomplete class

This code fails to compile because of the inheritance from an incomplete type (https://godbolt.org/z/G35wj9):

template<typename>
class Incomplete;

class T : public Incomplete<T> {};

template<typename>
class Incomplete {};

int main()
{
  [[maybe_unused]] T x;
}

I believed that this rule also applied to class templates. However, this code compiles fine (https://godbolt.org/z/cU6GNt):

template<typename>
class Incomplete;

template<int d>
class T : public Incomplete<T<d>> {};

template<typename>
class Incomplete {};

int main()
{
  [[maybe_unused]] T<1> x;
}

When class templates are involved, is the base class only required to be complete at the point of instantiation?

Upvotes: 3

Views: 481

Answers (3)

Sander De Dycker
Sander De Dycker

Reputation: 16243

The standard covers this in [temp.inst] §1 (taken from C++17) :

Unless a class template specialization has been explicitly instantiated (17.7.2) or explicitly specialized (17.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. [ Note: In particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and a conversion between pointers to class type depends on the inheritance relationship between the two classes involved. — end note ] [ Example:

template<class T> class B { /* ... */ };
template<class T> class D : public B<T> { /* ... */ };

void f(void*);
void f(B<int>*);

void g(D<int>* p, D<char>* pp, D<double>* ppp) {
    f(p);            // instantiation of D<int> required: call f(B<int>*)
    B<char>* q = pp; // instantiation of D<char> required: convert D<char>* to B<char>*
    delete ppp;      // instantiation of D<double> required
}

end example ] If a class template has been declared, but not defined, at the point of instantiation (17.6.4.1), the instantiation yields an incomplete class type (6.9). [ Example:

template<class T> class X;
X<char> ch;          // error: incomplete type X<char>

end example ] [ Note: Within a template declaration, a local class (12.4) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, noexcept-specifiers, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. — end note ]

Also covered on cppreference.

Just keep in mind that a class template is not a type, and no code is generated for it. Code is generated when the template is instantiated. When a class template is instantiated (implicitly or explicitly), an actual class (type) is generated (including the code for it).

Upvotes: 3

Xantopp
Xantopp

Reputation: 74

The only difference is, that in your example you use the Incomplete class inside of a template.

In the original example it was used by a class. The type of the class will be created by the compiler at this time, thus before Incomplete is defined.

In your code, Incomplete is used in another template T, (roughly speaking: T is a mold to generate various types by use of Incomplete). At this time the compiler does nothing, it just stores a "rule to generate types" (what I called the mold).
The compiler checks validity of T at the time it is used, hence at the line - then the actual type T<1> : public Incomplete gets generated. T (the mold) is "valid" iff Incomplete is defined

[[maybe_unused]] T<1> x;

At this point the template class Incomplete is well defined, and the compiler is happy.

Upvotes: 1

When class templates are involved, is the base class only required to be complete at the point of instantiation?

If it's a dependent base then yes. By that virtue a compiler has no idea what Incomplete<T<d>> is at the point of template definition. After all, for some values of d we can have ourselves a specialization of Incomplete<T<d>> that is entirely different from the primary template declaration.

template<>
class Incomplete<T<0>> {};

This is not a circular dependency. Simply naming the specialization T<0> does not cause it to be instantiated. It's just a type name. But it does mean the compiler has no recourse but wait until it can check the base class is valid.

On the other hand, if the base class is not a dependent type, then using it as base would be ill-formed.

Upvotes: 4

Related Questions