在 C++ 中进行复制初始化时实际发生了什么?

What actually happens when copy initializing in C++?

考虑以下代码:

#include<iostream>
using namespace std;
class A{
public:
  A()=default;
  A(int a){cout<<"A(int) called"<<endl;}
  A(const A&a);
  ~A(){cout<<"~A() called"<<endl;}
};
A::A(const A&a){
  cout<<"A(const A&) called"<<endl;
}
int main(){
   A a = 1;
}

当我用g++8.1编译时-fno-elide-constructors取消RVO,输出是:

A(int) called
A(const A&) called
~A() called
~A() called

我知道这是一个叫做转换构造函数的东西,从它的参数类型到它的类型的隐式转换class。

似乎首先由 A(int) 构造了一个临时对象,其次 a 对象是由复制构造函数 A(const A&).

构造的

但是当我修改我的代码时:

#include<iostream>
using namespace std;
class A{
public:
  A()=default;
  A(int a){cout<<"A(int) called"<<endl;}
  explicit A(const A&a); //use explicit
  ~A(){cout<<"~A() called"<<endl;}
};
A::A(const A&a){
  cout<<"A(const A&) called"<<endl;
}
int main(){
   //A a = A(1); //error:no constructor match
   A b = 1; //ok
}

令我困惑的是 b 对象是 显式 复制构造的?!即使我使用复制初始化?
当我通过 A(const A&a)=delete; 删除复制构造函数时,它不会按预期工作。

但是我用VS2017就不一样了。隐式转换 A a = 1; 与复制构造函数无关。即使我删除了cc,它也照常工作。

这里copy initialization的效果是,

(强调我的)

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion, which is a prvalue temporary (until C++17) prvalue expression (since C++17) if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)

请注意,ojbect 是从转换后的 A(来自 int)直接初始化的,复制构造函数是否标记为 explicit 在这里无关紧要。


顺便说一句:由于 mandatory copy elision,自 C++17 起,复制构造将被完全省略。

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

两者的区别

A a = A(1);

A b = 1;

是在第二种情况下,应用的效果是@songyuanyao 的回答中描述的效果。但是,第一种情况的效果是另一种:

If T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.

这就是为什么它不适用于 explicit 复制构造函数。