Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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*>?

like image 569
Peter Alexander Avatar asked Jun 13 '11 13:06

Peter Alexander


2 Answers

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*> {};
like image 200
Johannes Schaub - litb Avatar answered Nov 01 '22 04:11

Johannes Schaub - litb


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);

like image 22
Lightness Races in Orbit Avatar answered Nov 01 '22 04:11

Lightness Races in Orbit