你的推理有误。构造函数总是初始化所有基类和非静态成员(从技术上讲,虚拟基由最派生的类型初始化,而不是由任何其他基的构造函数初始化),因此 Base_class 实际上将由其编译器生成的默认构造函数初始化(或编译器生成的复制/移动构造函数,如果您正在执行复制或移动),它将使用其默认(或复制/移动)构造函数初始化所有数据成员。您可以稍后在具体类的构造函数中分配这些成员,但此时已经进行了初始化。
由于基类拥有所有数据成员,因此它实际上是在发生复制或移动时初始化所有数据成员的基类。如果您在最派生类中编写自己的复制或移动构造函数,则需要在初始化列表中调用基类的复制/移动构造函数,否则数据成员将是默认构造的,您将被迫事后使用复制/移动分配。这通常效率低下,在某些情况下可能是不正确的。(例如,我编写了可以移动构造的类,但由于切片问题而无法移动分配;如果您的 Base_class 中有这样一个类作为数据成员,则无法单独实现移动语义在 Concrete_class 中。)
如果必须从 Concrete_class 初始化所有数据成员,我建议您在 Base_class 中提供一个受保护的构造函数,该构造函数按值获取所有数据成员并将它们移动到自己的数据成员中,并在 Traits_class 中提供完美转发构造函数(或继承基类的构造函数,如果您使用的是具有此支持的编译器)。这允许具体类指定初始化数据成员的值,但允许基类进行实际初始化。它还允许 Base_class 和 Traits_class 构造函数访问完全初始化的数据成员(否则它们只能访问处于默认初始化状态的数据成员)。
这是一个例子:
struct Base_class {
protected:
Base_class( string s ) : s_( move(s) ) { }
~Base_class() = default;
// Request copy/move, since we're declaring our own (protected) destructor:
Base_class(Base_class const &) = default;
Base_class(Base_class &&) = default;
Base_class &operator=(Base_class const &) = default;
Base_class &operator=(Base_class &&) = default;
string s_;
};
template<int>
struct Traits_class : Base_class {
protected:
template<typename... P>
Traits_class( P &&p )
: Base_class( forward<P>(p)... )
{ }
};
template<int I>
struct Concrete_class : Traits_class<I> {
Concrete_class( char c )
: Traits_class<I>( string( I, c ) )
{ }
};
作为旁注,数据不一定必须在 Base_class 中才能让 Traits_class 能够访问它。如果您不介意虚函数调用的开销并且不需要在构造函数或析构函数中访问(我假设您不需要,因为当前数据在 Concrete_class 的构造函数运行之前,成员没有最终值)。或者,为了避免虚拟调用开销,您可以使用 @Yakk 提到的 Curiously Recurring Template Pattern。
== 回复在原始问题中编辑 ==
基类的构造函数将在派生类的构造函数之前运行,因此如果数据存储在派生类中,它将在基类的构造函数中未初始化(并且已经在其析构函数中回收)。您可以将构造函数视为获取基类的实例并将其转换为派生类的实例(通过初始化类的派生部分等),并且作为一种特殊情况,没有基类的构造函数类将“无”(原始存储)转换为类的实例。
因此,当您的特征类构造函数运行时,它还不是一个具体的派生类。因此,访问派生类的数据成员或以其他方式将该类用作派生类是非法的。该语言甚至对虚函数强制执行此操作;如果您在基类的构造函数或析构函数中调用虚函数,它将调用该函数的基版本。例子:
#include <iostream>
using namespace std;
struct Base {
Base() { cout << "Base ctor\n"; v(); }
~Base() { v(); cout << "Base dtor\n"; }
protected:
virtual void v() const { cout << "Base::v\n"; }
};
struct Derived : Base {
Derived() { cout << "Derived ctor\n"; v(); }
~Derived() { v(); cout << "Derived dtor\n"; }
protected:
virtual void v() const { cout << "Derived::v\n"; }
};
int main() { Derived d; }
/* Output:
Base ctor
Base::v
Derived ctor
Derived::v
Derived::v
Derived dtor
Base::v
Base dtor
*/