混淆在复制初始化和直接初始化 [英] Confusion in copy initialization and direct initialization

查看:158
本文介绍了混淆在复制初始化和直接初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑简单语句(取自复制初始化和直接初始化之间的C ++有什么区别?)

  A c2 = A ; 




此语句值初始化一个临时项,然后复制
值写入c2(读5.2.3 / 2和8.5 / 14)。 这当然需要
非显式复制构造函数(请阅读8.5 / 14和12.3.1 / 3和
13.3.1.3/1)


[注意上面的段落中的粗体句子] - >我的问题是为什么?



此代码:

  class B {}; 
struct A
{
A(B const&){}
A(A const&)= delete;
// A(A const&); //删除上面的语句,并取消注释这个语句,
//和一切正常,即使没有正文的复制构造函数Oo
};

A a2 = B(); //错误,因为没有复制构造函数oO

为什么复制初始化需要存在复制构造函数,



blockquote>

虽然直接初始化所有的构造函数都可以调用,
,另外可以做任何隐式转换,它需要匹配
参数类型,复制初始化只需设置一个隐式
转换序列


下面的中的粗体表示)



这并不意味着直接初始化可以访问所有构造函数,并且可以执行隐式转换序列,而复制初始化所有可以做的是执行隐式转换序列? 。我的意思是,直接初始化中的隐式转换不同于复制初始化中的隐式转换顺序?

解决方案

评估

  A a1 = B(); //(1)copy-initialization 
A a2 = {B()}; //(2)copy-initialization
A a3 {B()}; //(3)direct-initialization

来自 [dcl.init] / 17


- 如果初始化程序是(非括号) braced-init-list ,则对象或引用将被列表初始化(8.5.4)。

- [ ...]

- 如果目标类型是(可能是cv限定的)类类型:




  • 初始化是直接初始化,或者如果它是拷贝初始化,其中源类型的cv不合格的
    版本是与目的地的类相同的类,或者是目标类的类的派生类,
    构造函数。 [...]

  • 否则(即,对于剩余的复制初始化情况),用户定义的转换序列
    可以从源类型转换为目标类型, (当使用转换函数
    时)到其派生类,如13.3.1.4所述,最好的一个是通过重载分辨率(13.3)选择的
    。 [...]调用的结果(这是构造函数的临时结构)然后用于根据上面的规则直接初始化作为复制初始化目的地的对象
    。在某些情况下,允许实现
    通过将
    中间结果直接构造到正被初始化的对象中来消除这种直接初始化中固有的复制;请参阅12.2,12.8。 c $> $ <

    $ b c>和 a3 ,初始化器是一个braced-init-list ,所以我们只做list-initialization。这最终会调用 B const& 构造函数。



    对于 a1 ,第一个子项不适用 - 因为源类型( B )与目标类型不同或派生类( A )。因此,我们进入第二个子项目点,其中涉及考虑转换函数。有一个( A(B const&)),因此我们有效地重写了表达式

      A a1_new {A {B {}}}; 

    现在通常,这个额外的副本将被省略。但是你明确禁止它,所以代码无法编译。






    至于为什么我不知道。看起来像复制初始化应该只是直接初始化的语法糖。在大多数情况下,毕竟它是...


    Consider simple statement (Taken from Is there a difference in C++ between copy initialization and direct initialization?):

    A c2 = A();
    

    This statement value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.5/14). This of course will require a non-explicit copy constructor (Read 8.5/14 and 12.3.1/3 and 13.3.1.3/1)

    [Mind the bold sentence in above para] -> My question is why?

    Now consider this code :

    class B {};
    struct A 
    {
      A(B const&) {}
      A(A const&) = delete;
      //A(A const&); //delete above statement and uncomment this statement, 
      //and everything works, even though there in no body of copy constructor Oo
    };
    
    A a2 = B();    //error since there is no copy constructor oO
    

    Why copy-initialization requires presence of copy constructor even though it's not needed sometime as presented in above code

    Please please one more thing :

    While direct initialization has all constructors available to call, and in addition can do any implicit conversion it needs to match up argument types, copy initialization can just set up one implicit conversion sequence.

    [Mind the bolding in the following para]

    Doesn't that means direct initialization have access to all constructors and can perform implicit conversion sequence , while copy initialization all can do is perform implicit conversion sequence? . What I mean to ask is , implicit conversion in direct initialization is different from implicit conversion sequence in copy initialization ?

    解决方案

    The rules for evaluating

    A a1 = B();   // (1) copy-initialization
    A a2 = {B()}; // (2) copy-initialization
    A a3{B()};    // (3) direct-initialization
    

    come from [dcl.init]/17:

    — If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
    — [...]
    — If the destination type is a (possibly cv-qualified) class type:

    • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. [...]
    • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). [...] The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

    For both a2 and a3, the initializer is a braced-init-list, so we just do list-initialization. This ends up calling the B const& constructor.

    For a1, the first sub-bullet doesn't apply - since the source type (B) is not the same or derived class of the destination type (A). So we go into the second sub-bullet point which involves considering conversion functions. There is one (A(B const&)) so we effectively rewrite the expression

    A a1_new{A{B{}}};
    

    Now typically, this extra copy will be elided. But you're explicitly prohibiting it, so the code cannot compile.


    As to why the differentiation? I don't know. It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...

    这篇关于混淆在复制初始化和直接初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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