C ++ Наследование от неопределенного типа шаблона - PullRequest
5 голосов
/ 13 июня 2011

Этот код:

template <class T>
class Foo {};

typedef Foo<void*> Bar;

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

// use Foo<int*> somewhere.

Компилируется и отлично работает в MSVC 9.0, но не компилируется в GCC 4.1.1 или GCC 4.3.4 с ошибкой:

error: invalid use of undefined type 'class Bar'

Является ли это недопустимым C ++, который MSVC принимает неправильно, или ограничением GCC?

В любом случае, как можно обойти это, получить желаемое поведение: специализации указателей Foo, которые наследуются от неспециализированных Foo<void*>

Ответы [ 3 ]

5 голосов
/ 13 июня 2011

Вы не можете сделать это, кроме как путем написания специализации для всех T*, кроме случаев, когда T равно void.В противном случае вы извлечете класс из себя, который по понятным причинам не может работать.

Создание шаблона первичного класса для аргументов, имеющих явную или частичную специализацию, невозможно.Если вы попытаетесь, провоцируя создание экземпляра до того, как явная или частичная специализация станет видимой (обратите внимание, что ваш код не вызывал такое создание), ваша программа будет плохо сформирована, без необходимости диагностики (что фактически делает поведение неопределенным).

Для достижения вышеупомянутого решения можно использовать 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*> {};
.
3 голосов
/ 13 июня 2011

typedef - красная сельдь.

Следующий код эквивалентен:

template <class T>
class Foo {};

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

Должно быть ясно, что, хотя Foo<T*> объявлено в этот момент, оно не определено. И поэтому вы не можете использовать его в качестве основы.


[class.name] (редакция 2003 г., 9.1 / 2):

Определение класса вводит имя класса в область, в которой оно определено

[class.mem] (редакция 2003 г., 9.2 / 2):

Класс считается полностью определенный тип объекта (3.9) (или полный тип) при закрытии} спецификатор класса. В классе спецификация члена, класс считается завершенным в рамках функции тела, аргументы по умолчанию и конструктор ctor-инициализаторов (включая такие вещи во вложенных классы). В противном случае оно считается неполный в своем классе член-спецификация.

[class.derived] (редакция 2003 г., 10/1):

Имя класса в спецификаторе базы не должно быть не полностью определенным классом (раздел 9);

0 голосов
/ 13 июня 2011

Лучшим решением было бы составить Foo<void*>.В конце концов, вы не хотите, чтобы простой void* интерфейс загромождал ваши вещи, и вы не хотите, чтобы Foo<T*> был конвертируемым в Foo<void*>.

В качестве альтернативы, вы могли бы полностью специализировать Foo<void*> заранее.

Предполагая, конечно, что вы делаете это для свертывания типов, а не потому, что вы на самом деле хотите наследование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...