为了账号安全,请及时绑定邮箱和手机立即绑定

复制初始化和直接初始化之间有区别吗?

复制初始化和直接初始化之间有区别吗?

C++ C
智慧大石 2019-05-28 16:38:10
复制初始化和直接初始化之间有区别吗?假设我有这个功能:void my_test(){     A a1 = A_factory_func();     A a2(A_factory_func());     double b1 = 0.5;     double b2(0.5);     A c1;     A c2 = A();     A c3(A());}在每个分组中,这些陈述是否相同?或者在某些初始化中是否有额外的(可能是可优化的)副本?我见过有人说过这两件事。请引用文字作为证据。还请添加其他案例。
查看完整描述

3 回答

?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

C ++ 17更新

在C ++ 17中,A_factory_func()从创建临时对象(C ++ <= 14)变为仅指定此表达式初始化为(松散地说)在C ++ 17中的任何对象的初始化的含义。这些对象(称为“结果对象”)是由声明(如a1)创建的变量,在初始化最终被丢弃时创建的人工对象,或者如果引用绑定需要对象(例如,在A_factory_func();。最后一种情况下,一个对象是人工创建的,称为“临时实现”,因为A_factory_func()没有一个变量或引用,否则将需要一个对象存在)。

如在我们的情况下的例子,在的情况下,a1a2特别规则说,在这样的声明,相同的类型的一个prvalue初始化的结果对象a1是可变的a1,因此A_factory_func()直接初始化对象a1。任何中间函数式转换都不会产生任何影响,因为A_factory_func(another-prvalue)只是“传递”外部prvalue的结果对象也是内部prvalue的结果对象。


A a1 = A_factory_func();A a2(A_factory_func());

取决于什么类型A_factory_func()返回。我假设它返回一个A- 然后它正在做同样的事情 - 除了复制构造函数是显式的,然后第一个将失败。阅读8.6 / 14

double b1 = 0.5;double b2(0.5);

这样做是因为它是内置类型(这意味着这里不是类类型)。阅读8.6 / 14

A c1;A c2 = A();A c3(A());

这不是一样的。第一个默认初始化if A是非POD,并且不对POD进行任何初始化(Read 8.6 / 9)。第二个副本初始化:值初始化临时值,然后将该值复制到c2(读取5.2.3 / 28.6 / 14)。这当然需要一个非显式的复制构造函数(Read 8.6 / 1412.3.1 / 313.3.1.3/1)。第三个为函数创建一个函数声明,该函数c3返回一个A并且函数指针返回一个返回a的函数A(Read 8.2)。


深入研究初始化直接和复制初始化

虽然它们看起来相同并且应该做同样的事情,但在某些情况下这两种形式却截然不同。两种形式的初始化是直接和复制初始化:

T t(x);T t = x;

我们可以将行为归因于每个行为:

  • 直接初始化的行为类似于对重载函数的函数调用:在这种情况下,函数是T(包括explicit一些)的构造函数,参数是x。重载解析将找到最佳匹配构造函数,并在需要时将执行任何隐式转换。

  • 复制初始化构造一个隐式转换序列:它尝试转换x为类型的对象T。(然后它可以将该对象复制到to-initialized对象,因此也需要复制构造函数 - 但这在下面并不重要)

如您所见,复制初始化在某种程度上是关于可能的隐式转换的直接初始化的一部分:虽然直接初始化具有可用于调用的所有构造函数,并且此外可以进行任何隐式转换,它需要匹配参数类型,复制初始化可以设置一个隐式转换序列。

我努力尝试并获得以下代码,为每个表单输出不同的文本,而不使用“明显的”通过explicit构造函数。

#include <iostream>struct B;struct A { 
  operator B();};struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }};A::operator B() { std::cout << "<copy> "; return B(); }int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)}// output: <direct> <copy>

它是如何工作的,为什么输出结果呢?

  1. 直接初始化

    它首先对转换一无所知。它只会尝试调用构造函数。在这种情况下,以下构造函数可用且完全匹配

    B(A const&)

    调用该构造函数不需要转换,更不用说用户定义的转换了(请注意,此处也不会发生const限定转换)。所以直接初始化会调用它。

  2. 复制初始化

    如上所述,复制初始化将在a未输入B或从中导出时构建转换序列(这里显然是这种情况)。因此,它将寻找进行转换的方法,并将找到以下候选人

    B(A const&)operator B(A&);

    注意我是如何重写转换函数的:参数类型反映了this指针的类型,在非const成员函数中它是非const的。现在,我们将这些候选人x称为争论。获胜者是转换函数:因为如果我们有两个候选函数都接受对相同类型的引用,则较少的const版本获胜(顺便说一下,这也是非const成员函数调用非机制的机制) -const对象)。

    请注意,如果我们将转换函数更改为const成员函数,则转换是不明确的(因为它们都具有A const&当时的参数类型):Comeau编译器正确拒绝它,但GCC在非迂腐模式下接受它。但是切换到-pedantic它也会输出正确的模糊警告。

我希望这有助于使这两种形式的区别更加清晰!


查看完整回答
反对 回复 2019-05-28
?
慕桂英546537

TA贡献1848条经验 获得超10个赞

分配初始化不同。

以下两行都进行了初始化。完成一个构造函数调用:

A a1 = A_factory_func();  // calls copy constructorA a1(A_factory_func());   // calls copy constructor

但它并不等同于:

A a1;                     // calls default constructora1 = A_factory_func();    // (assignment) calls operator =

我目前没有文字来证明这一点,但实验起来很容易:

#include <iostream>using namespace std;class A {public:
    A() { 
        cout << "default constructor" << endl;
    }

    A(const A& x) { 
        cout << "copy constructor" << endl;
    }

    const A& operator = (const A& x) {
        cout << "operator =" << endl;
        return *this;
    }};int main() {
    A a;       // default constructor
    A b(a);    // copy constructor
    A c = a;   // copy constructor
    c = b;     // operator =
    return 0;}


查看完整回答
反对 回复 2019-05-28
?
皈依舞

TA贡献1851条经验 获得超3个赞

double b1 = 0.5; 是构造函数的隐式调用。

double b2(0.5); 是明确的电话。

查看以下代码以查看差异:

#include <iostream>class sss { public: 
  explicit sss( int ) 
  { 
    std::cout << "int" << std::endl;
  };
  sss( double ) 
  {
    std::cout << "double" << std::endl;
  };};int main() { 
  sss ddd( 7 ); // calls int constructor 
  sss xxx = 7;  // calls double constructor 
  return 0;}

如果你的类没有显式的构造,那么显式和隐式调用是相同的。


查看完整回答
反对 回复 2019-05-28
  • 3 回答
  • 0 关注
  • 698 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信