C ++ 17更新
在C ++ 17中,含义A_factory_func()
从创建一个临时对象(C ++ <= 14)变为仅指定对该表达式在C ++ 17中初始化为(宽松地说)的任何对象的初始化。这些对象(称为“结果对象”)是由声明(如a1
)创建的变量,是在初始化结束时被丢弃的人造对象,或者如果引用绑定需要对象(如中的in)A_factory_func();
。对象是人为创建的,称为“临时实现”,因为A_factory_func()
它没有变量或引用,否则将需要对象存在。
作为示例,在a1
和a2
特殊规则的情况下,在这样的声明中,同类型的prvalue初始化器的结果对象a1
是variable a1
,因此A_factory_func()
直接初始化object 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进行任何初始化(请参阅8.6 / 9)。第二个副本初始化:值-初始化一个临时值,然后将该值复制到c2
(读取5.2.3 / 2和8.6 / 14)。当然,这将需要非显式的副本构造函数(请参阅8.6 / 14和12.3.1 / 3和13.3.1.3/1)。第三个函数为c3
返回an 的函数创建函数声明A
,并使用指向返回a的函数的函数指针A
(读8.2)。
深入研究直接初始化和复制初始化
尽管它们看起来相同并且应该做相同的事情,但是在某些情况下,这两种形式却有很大不同。初始化的两种形式是直接初始化和复制初始化:
T t(x);
T t = x;
我们可以将行为归因于每个行为:
- 直接初始化的行为类似于对重载函数的函数调用:在这种情况下,函数是的构造函数
T
(包括explicit
),参数为x
。重载解析将找到最匹配的构造函数,并在需要时进行所需的任何隐式转换。
- 复制初始化构造了一个隐式转换序列:它尝试转换
x
为类型的对象T
。(然后,它可以将该对象复制到要初始化的对象中,因此也需要复制构造函数-但这在下面并不重要)
正如你看到的,拷贝初始化是在某些方面对于可能的隐式转换直接初始化的一部分:尽管直接初始化具有能调用所有构造函数,并且除了可以做到这一点需要匹配参数类型的任何隐式转换,复制初始化可以只设置一个隐式转换序列。
我尽力而为,得到以下代码为每种形式输出不同的文本,而无需通过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&);
请注意我如何重写转换函数:参数类型反映了this
指针的类型,在非const成员函数中,该指针指向非const。现在,我们将这些候选对象x
称为参数。赢家是转换函数:因为如果我们有两个候选函数都接受对相同类型的引用,则较少const版本会获胜(顺便说一句,这也是首选使用非const成员函数的机制将non称为-const对象)。
请注意,如果将转换函数更改为const成员函数,则转换将是模棱两可的(因为两者的参数类型均为A const&
then):Comeau编译器会正确拒绝它,但GCC在非non脚模式下接受它。切换到-pedantic
它也会输出适当的歧义警告。
我希望这有助于使这两种形式之间的区别更加清楚!
A c1; A c2 = c1; A c3(c1);
。