通过从函数 return 值移动的大括号初始化给出 "excess elements" 错误

Brace-initialization via move from function return value gives "excess elements" error

给定以下代码片段:

class Foo {};
Foo makeFoo() { return Foo{}; }

int main()
{
  Foo myFoo{makeFoo()};
}

我希望 main 中的单行声明 define/initialize myFoo 在 return 值上使用 Foo 的移动构造函数 makeFoo().

但是,我从 clang++ 3.5.1(在 C++14 模式下编译)得到以下错误:

error: excess elements in struct initializer
      Foo myFoo{makeFoo()};
                ^~~~~~~~~
1 error generated.

这是怎么回事? "struct initializer" 究竟是什么意思——它只是 POD 的默认(无参数)构造函数吗?为什么不调用移动构造函数?

由于Foo是聚合体,进行聚合体初始化。

N3797 §8.5.4 [dcl.init.list]/3:

List-initialization of an object or reference of type T is defined as follows:

  • If T is an aggregate, aggregate initialization is performed (8.5.1).

根据 N4296,这似乎已针对 C++17 进行了更改:

List-initialization of an object or reference of type T is defined as follows:

  • If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

毕竟没有"universal (or uniform) initialization syntax"这样的东西。列表初始化有一些特殊的行为。

对于您的情况,相关规则可在第 8.5.1 节中找到:

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

因此您的 class Foo 是一个 聚合

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.

这就是编译器解释代码的方式(正如@chris 指出的那样,在下一版本的 C++ 中,它不会这样做……尽管我认为这条规则也需要更新,仅仅 "as specified in 8.5.4" 并不足以阻止聚合初始化行为。

由于初始化器的数量多于成员,这是非法的。

An aggregate that is a class can also be initialized with a single expression not enclosed in braces, as described in 8.5.

这是允许 copy/move 初始化的规则。 Copy/move 聚合不能使用大括号。