派生类使用平凡构造函数的统一初始化

8

我正在试图理解一些与C++11统一初始化相关的边界情况,但我无法弄清楚为什么会出现这种情况:

struct Base
{
    int x,y,z;
};

struct Derived : Base
{
};
static_assert (std::is_trivial<Base>::value, "Base must be trivial");
static_assert (std::is_trivial<Derived>::value, "Derived must be trivial");

Base b{1, 2, 3};           // 1) This compiles fine
Derived d{10, 20, 30};     // 2) This fails

标记为 2 的行出现了错误,无论是在clang 3.1还是g++ 4.7中都会提示"no matching constructor for initialization of Derived"

我不明白为什么,在Derived的情况下,它试图调用一个构造函数而不是执行(我不知道如何称呼它,也许是聚合初始化?),就像第1行的情况一样。

以下推理中有什么问题吗?

A) 是平凡的,保证它可以静态初始化

B) 要进行静态初始化,就不能在运行时执行任何代码,因此不需要调用构造函数 A+B => 为什么它要在已知平凡的类型上调用构造函数呢?

我非常困惑......

1个回答

11

是否可以对某个对象进行初始化与其是否琐碎无关。重要的是你的 Derived 类型是否为聚合类型,但这并不是情况:

§8.5.1 [dcl.init.aggr] p1

一个聚合类型是指:没有用户提供的构造函数(12.1),非静态数据成员没有花括号或等号初始化器(9.2),没有私有或保护的非静态数据成员(第11条),没有基类(第10条)和没有虚函数(10.3)的数组或类(第9条)。

只有聚合类型可以使用聚合初始化方式进行初始化,因此列表初始化(即统一初始化)只能尝试寻找适合的构造函数。

您可以提供一个转发到基类的 constexpr 构造函数,并添加一个默认的 default 构造函数:

struct Derived : Base{
    Derived() = default;
    constexpr Derived(int a, int b, int c) : Base{a, b, c}{}
};

感谢您指出标准的相关部分。这确实解释了我的代码(和假设)的问题所在。但是,那么像“一个可以被静态初始化的平凡类型”(请参见http://en.wikipedia.org/wiki/C++11)这样的东西应该如何正确解释呢?当这个“平凡”的东西是一个派生类,并且因此根据8.5.1需要调用构造函数时,如何静态初始化它? - abigagli
通过将构造函数设置为constexpr - Matthieu M.
2
Matthieu就在这里,混淆可能在于静态初始化的含义,这与聚合初始化完全无关。 - David Rodríguez - dribeas
[...] struct X { int a,b; }; 具有平凡的默认复制构造函数。它没有任何接受1或2个整数的构造函数,但是因为它是一个聚合体,您可以在变量定义的地方设置值:X x = { 1,2 };。请注意,我故意避免使用花括号,原因有两个:语法不如特性重要,并且在C++11中,该语法可用于调用构造函数。 - David Rodríguez - dribeas
@David:啊,你是在谈论这个术语本身。关于“not”,我可能读错了。第一句话听起来有点奇怪,但看起来你在确认OP在他最后的评论中的假设。 - Xeo
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接