不可复制类型的复制列表初始化 [英] copy-list-initialization of non-copyable types

查看:135
本文介绍了不可复制类型的复制列表初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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初始化程序




T x = a;

b

以及参数传递,函数返回,抛出异常
(15.1),处理异常(15.3)和聚合成员
初始化(8.5.1)复制初始化。 [注意:
复制初始化可以调用移动(12.8)。 - end note]



15 - 表单中发生的初始化



T



以及新表达式(5.3.4),static_cast表达式
(5.2.9),功能符号类型转换(5.2.3)和base和
成员初始化器(12.6.2)被称为直接初始化


8.5。 4列表初始化[dcl.init.list]


1 - 列表初始化是对象或引用的初始化
a braced-init-list。这样的初始化程序称为初始化程序列表
和列表的逗号分隔的初始化子句称为初始化程序列表的
元素。初始化程序列表可能为空。
列表初始化可以在直接初始化或复制初始化中发生
contexts;在
直接初始化上下文中的列表初始化被称为直接列表初始化,并且在复制初始化上下文中的
列表初始化被称为
copy-list-initialization。




atomics的问题



29.6.5对原子类型操作的要求[atomics.types.operations.req]


code> #define ATOMIC_VAR_INIT(value)请参阅下文



宏扩展为适用于常量
初始化的令牌序列一个与值初始化兼容的
类型的静态存储持续时间的原子变量。 [注意:这个
操作可能需要初始化锁。 - end note]即使通过原子操作,也将$ b​​ $ b并发访问变量初始化,
构成数据竞争。 [示例:



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



EDIT





结论



因此,在很好的答案后, Nicol Bolas,有趣的结论是 complex g = {1,2} 是不复制的副本(它是复制初始化上下文)初始化解决像直接列表初始化),对于该标准建议有一个复制操作(12.6.1: ...并将其复制/移动到g )。 p>

FIX



提出请求: https://github.com/cplusplus/draft/pull/37

解决方案

  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}; ,顺序为:


  1. 初始值列表包含元素,因此此规则不计算。

  2. complex $ 不是 initializer_list 的特殊化c $ c>,因此此规则不计算。

  3. 根据13.3和13.3.1.7的规则,通过重载解析考虑适用的构造函数。这会找到需要两个双精度的构造函数。

因此,不会创建和复制/ b
$ b

复制列表初始化直接列表初始化之间的唯一区别在13.3.1.7 p1中说明:



在复制列表初始化中,如果选择显式构造函数,

这是复杂g {1,2} 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 g

8.5 Initializers

14 - The initialization that occurs in the form

T x = a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [Note: Copy-initialization may invoke a move (12.8). — end note ]

15 - The initialization that occurs in the forms

T x(a);

T x{a};

as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.

8.5.4 List-initialization [dcl.init.list]

1 - List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.

The problem with atomics

29.6.5 Requirements for operations on atomic types [atomics.types.operations.req]

#define ATOMIC_VAR_INIT(value) see below

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. [Note: This operation may need to initialize locks. — end note ] Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race. [ Example:

atomic<int> v = ATOMIC_VAR_INIT(5);

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 g

This 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:

The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.

  • If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).

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:

  1. The initializer list has elements, so this rule doesn't count.
  2. complex is not an aggregate, so this rule doesn't count.
  3. complex is not a specialization of initializer_list, so this rule doesn't count.
  4. 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:

[...] In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

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.

这篇关于不可复制类型的复制列表初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆