Peter Alexander
Peter Alexander

Reputation: 54290

C++ Inheriting from Undefined Template Type

This code:

template <class T>
class Foo {};

typedef Foo<void*> Bar;

template <class T>
class Foo<T*> : public Bar {};

// use Foo<int*> somewhere.

Compiles and works fine in MSVC 9.0, but doesn't compile in GCC 4.1.1 or GCC 4.3.4, with the error:

error: invalid use of undefined type 'class Bar'

Is this illegal C++ that MSVC accepts incorrectly, or a limitation of GCC?

Either way, how can I work around this get the desired behaviour: pointer specialisations of Foo that inherit from unspecialised Foo<void*>?

Upvotes: 5

Views: 1808

Answers (3)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507105

You cannot do that, except by writing the specialization for all T*, except when T is void. Otherwise, you will derive the class from itself, which for obvious reasons can't work.

Instantiating the primary class template for arguments that have an explicit or partial specialization is not possible. If you try to, by provoking an instantiation before the explicit or partial specialization is visible (note that your code did not provoke such an instantiation), your program is ill-formed, with no diagnostic being required (which effectively renders the behavior undefined).

To achieve the above work-around, you can use SFINAE

template <class T>
struct isnt_void { typedef void type; };

template<> struct isnt_void<void> { };

template <class T, class = void>
class Foo {};

template <class T>
class Foo<T*, typename isnt_void<T>::type> : public Foo<void*> {};

Upvotes: 5

Puppy
Puppy

Reputation: 146968

A superior solution would be to compose of Foo<void*>. After all, you don't want the raw void* interface cluttering up your stuff, and you don't want a Foo<T*> to be convertible to a Foo<void*>.

Alternatively, you could fully specialize Foo<void*> beforehand.

Assuming, of course, that you're doing this for type folding, instead of because you actually want inheritance.

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385264

The typedef is a red herring.

The following code is equivalent:

template <class T>
class Foo {};

template <class T>
class Foo<T*> : public Foo<void*> {};

It should be clear that, although Foo<T*> is declared at this point, it is not defined. And thus you may not use it as a base.


[class.name] (2003 wording, 9.1/2):

A class definition introduces the class name into the scope where it is defined

[class.mem] (2003 wording, 9.2/2):

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments and constructor ctor-initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

[class.derived] (2003 wording, 10/1):

The class-name in a base-specifier shall not be an incompletely defined class (clause 9);

Upvotes: 3

Related Questions