12.6.1 - 显式初始化
struct complex {
complex();
complex(double);
complex(double,double);
};
complex sqrt(complex,complex);
complex g = { 1, 2 }; // construct complex(1, 2)
// using complex(double, double)
// and *copy/move* it into g
8.5 初始化器
14 - 表单中发生的初始化
T x = a;
以及在参数传递、函数返回、抛出异常 (15.1)、处理异常 (15.3) 和聚合成员初始化 (8.5.1)中称为复制初始化。[注意:复制初始化可能会调用移动(12.8)。——尾注]
15 - 表单中发生的初始化
T x(a);
T x{a};
以及在 new 表达式 (5.3.4)、static_cast 表达式 (5.2.9)、函数符号类型转换 (5.2.3) 以及基类和成员初始化器 (12.6.2)中称为直接初始化。
8.5.4 列表初始化[dcl.init.list]
1 - 列表初始化是从一个花括号初始化列表初始化一个对象或引用。这样的初始化器称为初始化器列表,列表中以逗号分隔的初始化器子句称为初始化器列表的元素。初始化列表可能为空。列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表 初始化称为直接列表初始化,而复制初始化上下文中的列表初始化称为复制列表初始化。
原子问题
29.6.5 原子类型操作的要求 [atomics.types.operations.req]
#define ATOMIC_VAR_INIT(value)
见下文该宏扩展为一个令牌序列,适用于对与值初始化兼容的类型的静态存储持续时间的原子变量的常量初始化。[注意:此操作可能需要初始化锁。— end note ] 对正在初始化的变量的并发访问,即使是通过原子操作,也构成了数据竞争。[ 例子:
atomic<int> v = ATOMIC_VAR_INIT(5);
根据前面的部分,似乎不应该在没有涉及复制构造函数的情况下进行赋值初始化,即使它根据 §12.8.31 和 §12.8.32 被省略,但原子定义为:
29.5 原子类型 [atomics.types.generic]
atomic() noexcept = default;
constexpr atomic(T) noexcept;
atomic(const atomic&) = delete;
atomic& operator=(const atomic&) = delete;
atomic& operator=(const atomic&) volatile = delete;
T operator=(T) volatile noexcept;
T operator=(T) noexcept;
没有复制构造函数!
通常,ATOMIC_VAR_INIT
扩展为大括号表达式以进行大括号初始化,但atomic<int> v = {5}
仍然是赋值初始化,并且在直接构造临时对象后暗示复制构造。
我查看了“常量初始化”部分,看看是否存在允许在没有副本的情况下执行此操作的漏洞(因为“宏扩展为一个令牌序列,适合于常量初始化一个类型的静态存储持续时间的原子变量初始化兼容值”),但我已经放弃了。
相关讨论:
http://thread.gmane.org/gmane.comp.lib.qt.devel/8298
http://llvm.org/bugs/show_bug.cgi?id=14486
编辑
在构建扣除过程时引用相关标准部分的答案将是理想的。
结论
因此,在 Nicol Bolas 给出了很好的回答之后,有趣的结论是complex g = { 1, 2 }
标准建议的不复制(复制列表初始化像直接列表初始化一样解析)的副本(它是复制初始化上下文)有一个复制操作(12.6.1: ...and copy/move it into g
)。