通过从函数返回值移动进行大括号初始化会产生"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
中的单行使用Foo
对makeFoo()
的返回值的move构造函数来声明和定义/初始化myFoo
。
然而,我从clang++
3.5.1(在C++14模式下编译)中得到以下错误:
error: excess elements in struct initializer
Foo myFoo{makeFoo()};
^~~~~~~~~
1 error generated.
这是怎么回事?"结构初始化器"到底是什么意思——它只是POD的默认(无参数)构造函数吗?为什么没有调用move构造函数?
毕竟没有"通用(或统一)初始化语法"这回事。列表初始化有一些特殊的行为。
在您的情况下,相关规则见第8.5.1节:
聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),不包含基类(第10条),也不包含虚拟函数(10.3)。
因此,您的class Foo
是聚合。
当聚合由初始化器列表初始化时,如8.5.4中所述,初始化器列表的元素被视为聚合成员的初始化器,按下标或成员顺序递增。每个成员是从相应的初始值设定项子句复制初始化的。如果初始值设定项子句是一个表达式,并且需要进行收缩转换(8.5.4)来转换该表达式,则程序格式不正确。
这就是编译器解释代码的方式(正如@chris所指出的,在C++的下一个版本中,它不会这么做……尽管我认为这个规则也需要更新,但仅仅"按照8.5.4中的规定"并不足以停止聚合初始化行为)。
由于初始值设定项多于成员,因此这是非法的。
作为类的聚合也可以使用不包含在大括号中的单个表达式进行初始化,如8.5中所述。
这是允许复制/移动初始化的规则。复制/移动聚合不能使用大括号。
由于Foo
是一个聚合,因此执行聚合初始化。
N3797§8.5.4[dcl.init.list]/3:
类型T的对象或引用的列表初始化定义如下:
- 如果T是聚合,则执行聚合初始化(8.5.1)
根据N4296:,C++17似乎已经改变了这一点
类型T的对象或引用的列表初始化定义为如下:
- 如果T是类类型,并且初始值设定项列表具有类型cv U的单个元素,其中U是T或从T派生的类,对象从该元素初始化(通过复制初始化用于复制列表初始化,或通过直接初始化直接列表初始化)
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 通过从函数返回值移动进行大括号初始化会产生"excess elements"错误
- C++11 统一初始化'Excess elements in struct initializer'错误