通过从函数返回值移动来进行括号初始化会产生"多余元素"错误

Kyl*_*and 2 c++ move-semantics c++11 c++14 c++17

给出以下代码段:

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

int main()
{
  Foo myFoo{makeFoo()};
}
Run Code Online (Sandbox Code Playgroud)

我希望单行输入使用's move构造函数的返回值main来声明和定义/初始化.myFooFoomakeFoo()

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

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

这里发生了什么?"struct initializer"究竟意味着什么 - 它只是POD的默认(无参数)构造函数吗?为什么不调用移动构造函数?

Ben*_*igt 7

毕竟,没有"通用(或统一)初始化语法"这样的东西.列表初始化有一些特殊的行为.

在您的情况下,相关规则见8.5.1节:

聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3) ).

class Foo因此,您是一个聚合.

当初始化程序列表初始化聚合时,如8.5.4中所述,初始化程序列表的元素将作为聚合成员的初始化程序,增加下标或成员顺序.每个成员都是从相应的initializer子句复制初始化的.如果initializer子句是表达式并且转换表达式需要缩小转换(8.5.4),则程序格式错误.

这就是编译器解释你的代码的方式(正如@chris所说,在下一个版本的C++中,它不会这样做......虽然我认为这个规则也需要更新,但仅仅是" 8.5.4"实际上不足以停止聚合初始化行为".

由于初始化程序比成员多,因此是非法的.

作为类的聚合也可以使用未括在大括号中的单个表达式进行初始化,如8.5中所述.

这是允许复制/移动初始化的规则.复制/移动聚合不能使用大括号.