从花括号初始化列表直接初始化时的Copy-elision:哪个编译器正确?
关于C++初始化场景的标准合规性问题
在以下程序中,对象A a通过花括号初始化列表{A{}}完成直接初始化:
#include <iostream> struct A { int v = 0; A() {} A(const A &) : v(1) {} }; int main() { A a({A{}}); std::cout << a.v; }
MSVC和GCC编译运行后输出0,说明触发了拷贝消除(Copy-elision);而Clang输出1,执行了拷贝构造函数。
请问在此场景下,哪个编译器的行为符合C++标准?
回答
MSVC和GCC的行为符合C++17及以后版本的标准,Clang的行为不符合。
具体依据如下:
- C++17及以后的标准明确规定:当用同类型的纯右值(比如代码中的
A{}临时对象)初始化另一个同类型对象时,必须执行拷贝消除——编译器要直接在目标对象的内存位置构造该纯右值,完全跳过拷贝/移动构造函数的调用。 - 对于
A a({A{}})的场景,初始化器列表仅包含一个A类型的纯右值,根据列表初始化规则,这等效于用该纯右值直接初始化a,因此必须触发拷贝消除,最终a.v的值为默认构造的0。 - Clang在此场景下未执行强制拷贝消除,反而调用了拷贝构造函数导致
a.v变为1,不符合C++17及以后的标准要求。
若在C17之前的标准环境下,拷贝消除属于编译器可选的优化手段,此时三种编译器的行为都不违反标准。但当前主流编译器默认启用C17及以后标准,因此MSVC和GCC的行为是合规的。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

