gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 13:21:56 26 4
gpt4 key购买 nike

假设我有这个功能:

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)创建的变量,是在初始化结束时被丢弃的人造对象,或者如果引用绑定需要对象(如在A_factory_func();中)在最后一种情况下,是人为创建的对象,称为“临时实现”,因为A_factory_func()没有变量或引用,否则该变量或引用将需要对象存在。

例如,在a1a2的情况下,特殊规则说,在此类声明中,与a1相同类型的prvalue初始化程序的结果对象是变量a1,因此 直接初始化对象 A_factory_func()。任何中间函数式样式转换都不会产生任何效果,因为 a1只是“通过”外部prvalue的结果对象,同时也是内部prvalue的结果对象。



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


取决于 A_factory_func(another-prvalue)返回的类型。我假设它返回一个 A_factory_func()-然后执行相同的操作-除了当复制构造函数是显式的时,第一个将失败。阅读 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)。第二个副本初始化:值-初始化一个临时值,然后将该值复制到 A(读取 5.2.3/28.6/14)。当然,这将需要非显式的复制构造函数(读取 8.6/1412.3.1/313.3.1.3/1)。第三个为函数 c2创建一个函数声明,该函数声明返回 c3,并使用指向返回 A的函数的函数指针(读取 8.2)。



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

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

T t(x);
T t = x;


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


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


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

我努力尝试了 got the following code to output different text for each of those forms,没有通过 T构造函数使用“显而易见的”。

#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资格转换)。因此,直接初始化将调用它。
复制初始化

如上所述,当 explicit的类型不是 a或不是从其派生时,复制初始化将构造一个转换序列(这显然是这种情况)。因此它将寻找进行转换的方法,并找到以下候选者

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


注意我如何重写转换函数:参数类型反映 B指针的类型,在非const成员函数中,该指针指向非const。现在,我们以 this作为参数调用这些候选对象。赢家是转换函数:因为如果我们有两个候选函数都接受对相同类型的引用,则较少const版本会获胜(顺便说一句,这也是偏爱非const成员函数的机制要求non -const对象)。

请注意,如果我们将转换函数更改为const成员函数,则转换将是模棱两可的(因为这两个参数的类型均为 x):Comeau编译器会正确拒绝它,但GCC在非pedantic模式下接受它。切换到 A const&也会使其输出正确的歧义警告。


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

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

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com