Question

I was recently surprised to know that this code compiles (at least on gcc and MSVC++):

template<typename T>
class A {
public:
    T getT() { return T(); }
};

class B : public A<B> { };

When this doesn't:

class A;

class B : public A { };

class A {
public:
    B getB() { return B(); }
};

It seems weird to me that a template class could take an incomplete type as a template parameter and have a function that returned one by calling its constructor and still compile. So where exactly are complete types required (or if the list would be shorter, where are they not required)?

Was it helpful?

Solution

Following are the scenarios where Complete types are not required:

  • Declaring a member to be a pointer or a reference to the incomplete type.
  • Declaring functions which accepts/return incomplete types.
  • Defining functions which accept/return pointers/references to the incomplete type.
  • As a template type argument.

Basically You are fine using an Incomplete type, at any place where the compiler does not need to know the memory layout of the type.

As for the template type argument being allowed to be an Incomplete type, the Standard explicitly says so in 14.3.1 Template type arguments

OTHER TIPS

This is how CRTP works due to two stage template parsing. Template member functions are not parsed until their instantiation.

EDIT: Maybe, the wording is not very precise. What I meant to say that when compiler sees class B : public A< B > {...};, it goes through A< B >, notices that there is a function B get() {...}, but does not evaluate its definition, leaving it until function's actual instantiation, at which point B has to be a complete type.

EDIT: I believe, the exact rules are covered in Standard section 14.6 (as Als pointed out in his answer). It deals with dependent and non-dependent names and their resolution at different times during compilation according to two-phase template name look-up. However, unfortunately, the two-phase name look-up implementation can differ from Standard on different compilers. Same code might compile on GCC and might not on MSVC++ and vice versa. Even more, seemingly same code might be rejected by same compiler. On MSVC++ I had an issue when base class was using a pointer to derived class function as a default argument for its function. It did not compile under MSVC++ and compiled under GCC (correctly). However, using derived class constructor as a default parameter compiled with both compilers, even on MSVC++. Go figure.

A template isn't really code; it's a template that describes how to build the code, once you fill in the missing pieces (the type parameters). Because of this the compiler allows more leeway in a template definition than it does an actual code definition. When you actually use the template, with the types identified, the compiler needs to generate actual code and all the usual rules apply.

A full definition isn't required if the compiler doesn't need to know the size or offsets to members of the object. Defining a pointer or reference to a class for example doesn't need either of those. At the point where you try to use the pointer or reference you will need a full definition.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top