C++17 更新
在 C++17 中,A_factory_func() 的含义从创建临时对象 (C++a1),在初始化最终被丢弃时创建的人造对象,或者引用绑定需要一个对象(如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/2 和8.6/14)。这当然需要一个非显式的复制构造函数(阅读 8.6/14 和 12.3.1/3 和 13.3.1.3/1 )。第三个为函数c3 创建一个函数声明,该函数返回一个A,并接受一个函数指针,该函数指针指向一个返回A 的函数(阅读8.2)。
深入研究初始化直接和复制初始化
虽然它们看起来相同并且应该做相同的事情,但这两种形式在某些情况下却截然不同。初始化的两种形式分别是直接初始化和复制初始化:
T t(x);
T t = x;
我们可以将它们归因于每个行为:
- 直接初始化的行为类似于对重载函数的函数调用:在这种情况下,函数是
T 的构造函数(包括explicit 的构造函数),参数是x。重载解析将找到最佳匹配的构造函数,并在需要时进行所需的任何隐式转换。
- 复制初始化构造一个隐式转换序列:它尝试将
x 转换为T 类型的对象。 (然后它可能会将该对象复制到要初始化的对象中,因此也需要一个复制构造函数——但这在下面并不重要)
如您所见,复制初始化在某种程度上是直接初始化的一部分,涉及可能的隐式转换:虽然直接初始化有所有可调用的构造函数,此外 em> 可以进行任何需要匹配参数类型的隐式转换,复制初始化只需设置一个隐式转换序列。
我努力尝试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&);
注意我是如何重写转换函数的:参数类型反映了this 指针的类型,它在非常量成员函数中是非常量的。现在,我们用x 作为参数来称呼这些候选人。获胜者是转换函数:因为如果我们有两个候选函数都接受对同一类型的引用,那么 less const 版本获胜(顺便说一下,这也是首选非-const 成员函数调用非常量对象)。
请注意,如果我们将转换函数更改为 const 成员函数,则转换是不明确的(因为两者都有一个参数类型 A const& 然后):Comeau 编译器正确拒绝它,但 GCC 在非-迂腐模式。不过,切换到-pedantic 也会使其输出正确的歧义警告。
我希望这有助于更清楚地了解这两种形式的不同之处!