问题描述
12.6.1 - 显式初始化
struct complex {
complex ;
complex(double);
complex(double,double);
};
complex sqrt(complex,complex);
complex g = {1,2}; //构造复杂(1,2)
//使用复合(双,双)
//和* copy / move * it into g
pre>
8.5初始化程序
8.5。 4列表初始化[dcl.init.list]
atomics的问题
29.6.5对原子类型操作的要求[atomics.types.operations.req]
根据前面的章节,没有复制构造函数,即使它根据§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} 仍然是一个赋值初始化,并暗示在直接构造一个临时函数之后进行拷贝构建。
常量初始化部分,以查看是否有一个漏洞允许这没有副本(因为宏扩展到适合于恒定初始化的静态存储持续时间的类型的初始化兼容的值的原子变量的令牌序列 )
EDIT
结论
因此,在很好的答案后, Nicol Bolas,有趣的结论是 complex g = {1,2} 是不复制的副本(它是复制初始化上下文)初始化解决像直接列表初始化),对于该标准建议有一个复制操作(12.6.1: ...并将其复制/移动到g )。 p>
FIX
提出请求:
解决方案complex g = {1,2}; //构造复杂(1,2)
//使用复合(双,双)
//和* copy / move * it into g
pre>
这不是真的。我不是说复制/移动将被省略;
您引用了8.5 p14,它定义了 T x = a; 作为复制初始化。这是真的。但它接下来定义初始化如何工作:
从8.5开始,p16:
右边意味着复制初始化规则不适用于 braced-init-list 。他们使用一组单独的规则,如8.5.4中所述。
你引用了8.5.4,它定义了 T x = {.. 。}; 作为 copy-list-initialization 。您的推论出错的地方是,您从未查找过 copy-list-initialization 实际上 。没有复制; 是列表初始化的子集
。因此,它遵循8.5.4,p3规定的所有规则。我不会在这里引用他们,因为他们有几页长。我将简单解释规则如何应用于 complex g = {1,2}; ,顺序为:
- 初始值列表包含元素,因此此规则不计算。
- complex $ 不是 initializer_list $>的特殊化c $ c>,因此此规则不计算。
- 根据13.3和13.3.1.7的规则,通过重载解析考虑适用的构造函数。这会找到需要两个双精度的构造函数。
因此,不会创建和复制/ b
$ b复制列表初始化和直接列表初始化之间的唯一区别在13.3.1.7 p1中说明:
在复制列表初始化中,如果选择显式构造函数,
这是复杂g {1,2} $ c $之间的 c>和 complex g = {1,2} 。它们都是 list-initialization 的例子,它们以统一的方式工作,除了使用显式构造函数。
12.6.1 - Explicit initialization
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 g8.5 Initializers
8.5.4 List-initialization [dcl.init.list]
The problem with atomics
29.6.5 Requirements for operations on atomic types [atomics.types.operations.req]
According to previous sections it seems there shouldn't be assignment initialization without a copy-constructor involved, even if it's elided according to §12.8.31 and §12.8.32, but atomics are defined as:
29.5 Atomic types [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;There's no copy-constructor!
Frequently, ATOMIC_VAR_INIT expands to a brace expression for brace initialization, but atomic<int> v = {5} is still an assignment initialization and would imply copy construction after direct construction of a temporary.
I've looked over the "constant initialization" section to see whether there's a loophole allowing this without a copy (because of "The macro expands to a token sequence suitable for constant initialization of an atomic variable of static storage duration of a type that is initialization-compatible with value") but I'm already giving up.
Related discussions:
http://thread.gmane.org/gmane.comp.lib.qt.devel/8298
http://llvm.org/bugs/show_bug.cgi?id=14486
EDIT
An answer quoting the relevant standard sections while building a deduction process would be ideal.
CONCLUSION
So, after the nice answer by Nicol Bolas, the funny conclusion is that complex g = { 1, 2 } is a copy (it is copy-initialization context) which don't copy (copy-list-initialization resolves like direct-list-initialization) for which the standard suggests there's a copy operation (12.6.1: ...and copy/move it into g).
FIX
Pull request: https://github.com/cplusplus/draft/pull/37
解决方案complex g = { 1, 2 }; // construct complex(1, 2) // using complex(double, double) // and *copy/move* it into gThis is untrue. And I'm not saying that the copy/move will be elided; I mean that there will be no copying or moving.
You quoted 8.5 p14, which defines T x = a; as copy-initialization. This is true. But it then goes on to define how initialization actually works:
From 8.5, p16:
That right there means that copy-initialization rules do not apply to a braced-init-list. They use a separate set of rules, as covered in 8.5.4.
You quoted 8.5.4, which defines T x = {...}; as copy-list-initialization. Where your reasoning goes wrong is that you never looked up what copy-list-initialization actually does. There is no copying; that's just what it's called.
copy-list-initialization is a subset of list-initialization. Therefore, it follows all of the rules laid down by 8.5.4, p3. I'm not going to quote them here, because they're several pages long. I'll simply explain how the rules apply to complex g = {1, 2};, in order:
- The initializer list has elements, so this rule doesn't count.
- complex is not an aggregate, so this rule doesn't count.
- complex is not a specialization of initializer_list, so this rule doesn't count.
- Applicable constructors are considered via overload resolution, in accord with the rules of 13.3 and 13.3.1.7. This finds the constructor that takes two doubles.
Therefore, no temporary will be created and copied/moved in.
The only difference between copy-list-initialization and direct-list-initialization is stated in 13.3.1.7, p1:
That is the only difference between complex g{1, 2} and complex g = {1, 2}. They are both examples of list-initialization, and they work in a uniform way except for the use of explicit constructors.
这篇关于不可复制类型的复制列表初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!