Вызов базового конструктора шаблона при ошибке списка инициализации элемента - PullRequest
12 голосов
/ 17 января 2012

У меня есть базовый класс, который выглядит следующим образом.

template<typename T>
class Base
{
   public:
      Base(int someValue);

      virtual T someFunc() =0;
};

template<typename T>
Base<T>::Base(int someValue)
{}

А потом следующее.

#include "base.hpp"

class Foo
   : public Base<Foo>
{
   public:
      Foo(int someValue);

      virtual Foo someFunc();
};

Foo::Foo(int someValue)
   : Base(someValue)
{}

Я получаю следующую ошибку от gcc 4.2.1.

error: class ‘Foo’ does not have any field named ‘Base’

Я должен упомянуть, что это прекрасно компилируется на моей машине Fedora, которая работает под управлением gcc 4.6.2 Эта ошибка возникает при компиляции на моем компьютере OS X Lion.

Заранее спасибо за помощь.

EDIT

Проблема в том, что я не указываю тип шаблона в классе Foo при вызове конструктора. Следующее исправляет ошибку в os x.

: Base<Foo>(someValue, parent)

EDIT

Да, это похоже на ошибку. То, что я упоминал ранее, исправляет ошибку в OS X, и код исправляется в Fedora с этим исправлением. Пойду посмотрю, есть ли обновление для gcc в os x.

1 Ответ

9 голосов
/ 17 января 2012

Первый:

[C++11: 12.6.2/3]: A mem-initializer-list может инициализировать базовый класс, используя любой class-or-decltype , который обозначает, чтотип базового класса.

[ Пример:

struct A { A(); };
typedef A global_A;
struct B { };
struct C: public A, public B { C(); };
C::C(): global_A() { } // mem-initializer for base A

- конечный пример ]

И Base должно быть действительным имя введенного класса для базы здесь (то есть вы можете использовать ее вместо Base<T>):

[C++11: 14.6.1/1]: Как и обычные (не шаблонные) классы, шаблоны классов имеют injected-class-name (раздел 9). имя введенного класса может использоваться как имя шаблона или имя типа . Когда оно используется с шаблон-аргумент-список , как шаблон-аргумент для шаблона шаблон-параметр или как окончательный идентификатор в подробный спецификатор типа объявления шаблона класса друга, оно относится к самому шаблону класса.В противном случае, это эквивалентно имени шаблона , за которым следуют параметры-шаблона шаблона класса, заключенного в <>.

[C++11: 14.6.1/3]: injected-class-name шаблона класса или специализации шаблона класса может использоваться как template-name или имя типа , где бы оно ни находилось.[ Пример:

template <class T> struct Base {
   Base* p;
};

template <class T> struct Derived: public Base<T> {
   typename Derived::Base* p; // meaning Derived::Base<T>
};

template<class T, template<class> class U = T::template Base> struct Third { };
Third<Base<int> > t; // OK: default argument uses injected-class-name as a template

- конец примера ]

Я не нашел ничего, что указывало бы, что это неприменить в ctor-initializer , так что я бы сказал, что это ошибка компилятора.

Мой урезанный тестовый сценарий терпит неудачу в GCC 4.1.2 и GCC 4.3.4 , но успешно в GCC 4.5.1 (режим C ++ 11) .Похоже, что это устраняется с помощью GCC bug 189 примечания к выпуску GCC 4.5 :

G ++ теперь реализует DR 176 .Ранее G ++ не поддерживал использование inject-class-name базового класса шаблона в качестве имени типа, и поиск имени нашел объявление шаблона во вложенной области видимости.Теперь поиск имени находит имя введенного класса, которое можно использовать как тип или как шаблон, в зависимости от того, следует ли за именем список аргументов шаблона.В результате этого изменения, некоторый ранее принятый код может быть неверно сформирован, потому что

  • Имя введенного класса недоступно, потому что оно из частной базы, или
  • Введенное имя класса нельзя использовать в качестве аргумента для параметра шаблона шаблона.

В любом из этих случаев код можно исправить, добавив спецификатор вложенного имени для явного именишаблон.Первый можно обойти с помощью -fno-access-control;второй отклоняется только с -pedantic.


Мой урезанный тестовый пример с абстрагированным Qt:

template <typename T>
struct Base { };

struct Derived : Base<Derived> { // I love the smell of CRTP in the morning
   Derived();
};

Derived::Derived() : Base() {};
...