不可复制类型的复制列表初始化
copy-list-initialization of non-copyable types
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};
以及在新的表达式(5.3.4)、static_cast表达式中(5.2.9),函数表示法类型转换(5.2.3),以及基础和成员初始化程序(12.6.2)称为直接初始化。
8.5.4列表初始化[dcl.init.List]
1-列表初始化是从一个有支撑的init列表。这样的初始化器被称为初始化器列表,并且列表中以逗号分隔的初始值设定项子句被称为初始值设定项列表的元素。初始值设定项列表可能为空。列表初始化可以发生在直接初始化或复制初始化中语境;中的列表初始化直接初始化上下文称为直接列表初始化调用复制初始化上下文中的列表初始化复制列表初始化。
原子学的问题
29.6.5对原子类型的操作要求[atomics.types.operations.req]
#define ATOMIC_VAR_INIT(value)
参见下面的宏扩展为适用于常量的令牌序列的静态存储持续时间的原子变量的初始化初始化与值兼容的类型。[注:此操作可能需要初始化锁。--结束注释]并发访问即使通过原子操作,构成了数据竞赛。[示例:
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
)。
修复
拉取请求:https://github.com/cplusplus/draft/pull/37
complex g = { 1, 2 }; // construct complex(1, 2)
// using complex(double, double)
// and *copy/move* it into g
这是不真实的。我并不是说复制/移动会被取消;我的意思是不会有复制或移动。
您引用了8.5 p14,它将T x = a;
定义为复制初始化。这是真的。但它接着定义了初始化的实际工作方式:
从8.5开始,第16页:
初始化程序的语义如下。目标类型是要初始化的对象或引用的类型,源类型是初始化项表达式的类型。如果初始值设定项不是单个(可能是带括号的)表达式,则不定义源类型。
- 如果初始值设定项是一个(非括号)支撑的初始列表,则对象或引用被列表初始化(8.5.4)
这意味着复制初始化规则不会将应用于支持的初始化列表。它们使用一套单独的规则,如8.5.4所述。
您引用了8.5.4,其中将T x = {...};
定义为复制列表初始化。您的推理出错的地方是,您从未查找复制列表初始化实际执行的操作。没有复制;这就是所称的。
复制列表初始化是列表初始化 因此,不会创建和复制/移入临时文件。 复制列表初始化和直接列表初始化之间的唯一区别如13.3.1.7,p1:所述 〔…〕在复制列表初始化中,如果选择了显式构造函数,则初始化格式错误。 这是complex
不是聚合,因此此规则不计算在内complex
不是initializer_list
的专门化,因此此规则不计算在内complex g{1, 2}
和complex g = {1, 2}
之间的唯一区别list-initialization
的示例,除了使用显式构造函数外,它们以统一的方式工作。
-T
的构造函数是而不是显式的,复制列表初始化与复制初始化不同。两者都会导致"考虑构造函数",但副本初始化总是"考虑"副本的缺点;structor,而列表初始化则考虑填充了列表元素的构造函数(加上一些细节)。也就是说:
struct Foo
{
Foo(int) {}
Foo(Foo const &) = delete;
};
int main()
{
Foo f = { 1 }; // Fine
}
(如果构造函数是explicit
,这将失败。此外,由于复制构造函数被删除,Foo x = 1;
当然会失败。)
也许还有一个更具启发性的用例:
Foo make() { return { 2 }; }
void take(Foo const &);
take(make());
8.5.4/3和13.3.1.7/1中规定了所有必要的内容。
- 复制列表初始化的隐式转换的等级是多少
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- C++11 中的混合列表初始化
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- C++20 从括号中的值列表初始化聚合,不支持内部数组
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 默认参数和空列表初始化
- 如何在列表初始化中放置额外的语句?
- C++列表初始化允许多个用户定义的转换
- 列表初始化是否将原子初始化为零
- 使用可变模板列表初始化数组,并放置new
- 使用整数初始化列表初始化长双精度的向量
- 直接列表初始化的自动规则
- 使用初始化列表初始化unique_ptr的容器,继续
- 如何修复"非聚合无法使用初始值设定项列表初始化" <map>
- 直接列表初始化和复制列表初始化之间的差异
- 为什么我可以在不使用赋值运算符的情况下使用列表初始化普通数组
- C++ - 使用类中的初始值设定项列表初始化动态集