c++ - 复制初始化和直接初始化有区别吗?

标签 c++ initialization

假设我有这个功能:

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());
}

在每个分组中,这些陈述是否相同?或者在某些初始化中是否有额外的(可能可优化的)拷贝?

我见过人们说这两件事。请引用 文字为证。还请添加其他案例。

最佳答案

C++17 更新

在C++17中,A_factory_func()的含义从创建临时对象 (C++<=14) 更改为仅指定此表达式在 C++17 中初始化为(粗略地说)的任何对象的初始化。这些对象(称为“结果对象”)是由声明创建的变量(如 a1 )、初始化结束时创建的人工对象或引用绑定(bind)需要一个对象(如 A_factory_func(); 中) . 在最后一种情况下,一个对象是人为创建的,称为“临时物化”,因为 A_factory_func() 没有变量或引用,否则需要对象存在)。

作为我们的例子,在 a1 的情况下和 a2特殊规则说,在这样的声明中,与 a1 类型相同的纯右值初始值设定项的结果对象。是可变的 a1 ,因此 A_factory_func()直接初始化对象a1 .任何中间函数式类型转换都不会产生任何效果,因为 A_factory_func(another-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());

这不是在做同样的事情。如果 A,则第一个默认值初始化是非 POD,不会对 POD 进行任何初始化(阅读 8.6/9)。第二个拷贝初始化:值初始化一个临时值,然后将该值复制到 c2 (阅读 5.2.3/28.6/14)。这当然需要一个非显式复制构造函数(阅读 8.6/1412.3.1/313.3.1.3/1)。第三个为函数 c3 创建函数声明返回 A这需要一个函数指针,指向一个返回 A 的函数。 (阅读 8.2)。

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

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

我们可以将行为归因于它们中的每一个:
  • 直接初始化的行为就像对重载函数的函数调用:在这种情况下,函数是 T 的构造函数。 (包括 explicit 个),参数为 x .重载解析将找到最匹配的构造函数,并在需要时进行任何所需的隐式转换。
  • 复制初始化构造了一个隐式转换序列:它试图转换 xT 类型的对象. (然后它可能会将该对象复制到要初始化的对象中,因此也需要一个复制构造函数 - 但这在下面并不重要)

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

    我努力了 got the following code to output different text for each of those forms , 不使用“明显”通过 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>
    

    它是如何工作的,为什么会输出该结果?
  • 直接初始化

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

    调用该构造函数不需要任何转换,更不用说用户定义的转换(请注意,这里也没有发生 const 限定转换)。所以直接初始化会调用它。
  • 复制初始化

    如上所述,复制初始化会在a时构造一个转换序列。没有输入B或从它派生出来(这里显然就是这种情况)。所以它会寻找进行转换的方法,并会找到以下候选者
    B(A const&)
    operator B(A&);
    

    注意我是如何重写转换函数的:参数类型反射(reflect)了 this 的类型。指针,在非常量成员函数中指向非常量。现在,我们用 x 来称呼这些候选人。作为论据。赢家是转换函数:因为如果我们有两个候选函数都接受对同一类型的引用,那么较少的 const 版本获胜(顺便说一句,这也是喜欢非常量成员函数调用非-const 对象)。

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

  • 我希望这有助于更清楚地说明这两种形式的不同之处!

    关于c++ - 复制初始化和直接初始化有区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1051379/

    相关文章:

    c++ - 访问其他程序并改变其行为(主要是关于游戏的非官方多人模式)

    c++ - 字典的动态内存分配

    C++结构数组初始化

    ruby-on-rails-3.2 - 在自定义初始化程序中使用模块时未初始化的常量

    java - 构造函数错误

    c++ - 自适应阈值opencv

    c++ - 外部(人)问题

    c# - 查找可以用给定字母集拼写的单词集

    c++ - 在 C++ 中使用文件句柄的条件

    c++ - 编译后确定静态初始化顺序?