关于c ++:模板基础构造函数调用成员初始化列表错误

Template base constructor call in member initialization list error

我有一个基本类,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
template<typename T>
class Base
{
   public:
      Base(int someValue);

      virtual T someFunc() =0;
};

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

然后是下面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include"base.hpp"

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

      virtual Foo someFunc();
};

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

我从GCC4.2.1中得到以下错误。

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

我应该提到,我的软顶帽盒子上的这个编译很好,运行GCC4.6.2。在我的OS X Lion计算机上编译时发生此错误。

事先谢谢你的帮助。

编辑

问题似乎是我在调用构造函数时没有指示foo类中模板的类型。以下修复了OS X中的错误。

1
: Base<Foo>(someValue, parent)

编辑

是的,这看起来确实像个虫子。我之前提到的修复了OSX下的错误,并且代码在Fedora中可以通过该修复进行编译。会去看看在OSX中是否有对gcc的更新。


第一:

[C++11: 12.6.2/3]: A mem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.

[ Example:

1
2
3
4
5
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 example ]

并且Base应该是这里基础的一个有效的注入类名(也就是说,可以用它代替Base):

[C++11: 14.6.1/1]: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier
of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

[C++11: 14.6.1/3]: The injected-class-name of a class template or class template specialization can be used either as a template-name or a type-name wherever it is in scope. [ Example:

1
2
3
4
5
6
7
8
9
10
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 example ]

我没有发现任何东西表明这在ctor初始值设定项中不适用,所以我认为这是一个编译器错误。

我的剥离测试用例在GCC 4.1.2和GCC 4.3.4中失败,但是在GCC 4.5.1(C++ 11模式)中成功。这似乎是由GCC bug 189解决的;在GCC 4.5发行说明中:

G++ now implements DR 176. Previously G++ did not support using the
injected-class-name of a template base class as a type name, and
lookup of the name found the declaration of the template in the
enclosing scope. Now lookup of the name finds the injected-class-name,
which can be used either as a type or as a template, depending on
whether or not the name is followed by a template argument list. As a
result of this change, some code that was previously accepted may be
ill-formed because

  • The injected-class-name is not accessible because it's from a private base, or
  • The injected-class-name cannot be used as an argument for a template template parameter.

In either of these cases, the code can be fixed by adding a
nested-name-specifier to explicitly name the template. The first can
be worked around with -fno-access-control; the second is only rejected
with -pedantic.

我剥离了qt提取出来的测试用例:

1
2
3
4
5
6
7
8
template <typename T>
struct Base { };

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

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