Шаблон базового конструктора вызывает ошибку члена инициализации

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

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.

Заранее благодарим за помощь.

ИЗМЕНИТЬ

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

: Base<Foo>(someValue, parent)

ИЗМЕНИТЬ

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

Ответ 1

Во-первых:

[C++11: 12.6.2/3]: Список mem-initializer может инициализировать базовый класс, используя любой класс или-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

-end пример]

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

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

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

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

-end пример]

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

Мой усеченный тестовый файл не работает в GCC 4.1.2 и GCC 4.3.4, но преуспевает в GCC 4.5.1 (режим С++ 11). Кажется, это разрешено ошибка GCC 189; в примечания к выпуску GCC 4.5:

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

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

В любом из этих случаев код может быть исправлен путем добавления inested-name-specifier, чтобы явно указать шаблон. Первый может работать с -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() {};