c++ 17更新
In C++17, the meaning of A_factory_func() changed from creating a temporary object (C++<=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects (called "result objects") are the variables created by a declaration (like a1), artificial objects created when the initialization ends up being discarded, or if an object is needed for reference binding (like, in A_factory_func();. In the last case, an object is artificially created, called "temporary materialization", because A_factory_func() doesn't have a variable or reference that otherwise would require an object to exist).
以我们的例子为例,在a1和a2的情况下,特殊的规则说,在这样的声明中,与a1相同类型的prvalue初始化器的结果对象是变量a1,因此A_factory_func()直接初始化对象a1。任何中间函数风格的强制转换都不会产生任何影响,因为A_factory_func(另一个-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());
This is not doing the same. The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ). The third creates a function declaration for a function c3 that returns an A and that takes a function pointer to a function returning a A (Read 8.2).
深入研究初始化直接和复制初始化
虽然它们看起来一模一样,而且应该做同样的事情,但在某些情况下,这两种形式有显著的不同。初始化有两种形式:直接初始化和复制初始化:
T t(x);
T t = x;
我们可以把它们各自的行为归结为:
Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required.
Copy initialization constructs an implicit conversion sequence: It tries to convert x to an object of type T. (It then may copy over that object into the to-initialized object, so a copy constructor is needed too - but this is not important below)
如你所见,复制初始化在某种程度上是直接初始化的一部分,涉及到可能的隐式转换:直接初始化有所有可用的构造函数可调用,而且还可以进行匹配参数类型所需的任何隐式转换,而复制初始化只能设置一个隐式转换序列。
我努力尝试并得到了以下代码,为每一种形式输出不同的文本,而不使用“明显的”通过显式构造函数。
#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>
它是如何工作的,为什么输出这个结果?
Direct initialization
It first doesn't know anything about conversion. It will just try to call a constructor. In this case, the following constructor is available and is an exact match:
B(A const&)
There is no conversion, much less a user defined conversion, needed to call that constructor (note that no const qualification conversion happens here either). And so direct initialization will call it.
Copy initialization
As said above, copy initialization will construct a conversion sequence when a has not type B or derived from it (which is clearly the case here). So it will look for ways to do the conversion, and will find the following candidates
B(A const&)
operator B(A&);
Notice how I rewrote the conversion function: The parameter type reflects the type of the this pointer, which in a non-const member function is to non-const. Now, we call these candidates with x as argument. The winner is the conversion function: Because if we have two candidate functions both accepting a reference to the same type, then the less const version wins (this is, by the way, also the mechanism that prefers non-const member function calls for non-const objects).
Note that if we change the conversion function to be a const member function, then the conversion is ambiguous (because both have a parameter type of A const& then): The Comeau compiler rejects it properly, but GCC accepts it in non-pedantic mode. Switching to -pedantic makes it output the proper ambiguity warning too, though.