C++ 在C+中初始化复制时实际会发生什么+;?

C++ 在C+中初始化复制时实际会发生什么+;?,c++,explicit,copy-initialization,C++,Explicit,Copy Initialization,考虑以下代码: #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){

考虑以下代码:

#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;
}
我知道这是一种叫做转换构造函数的东西,它是从参数类型到类类型的隐式转换

似乎首先是由
a(int)
构造临时对象,其次是由复制构造函数
a(const a&)
构造
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
}
#包括
使用名称空间std;
甲级{
公众:
A()=默认值;
这里的效应是

(强调矿山)

如果T是类类型,并且other类型的cv非限定版本不是T或从T派生,或者如果T是非类类型,但other类型是类类型,则用户定义的转换序列可以从other类型转换为T(或者如果T是类类型且有转换函数可用,则转换为从T派生的类型)通过重载解析选择最佳的。转换的结果是一个
prvalue临时表达式(直到C++17)
prvalue表达式(从C++17开始)
如果使用了转换构造函数,然后用于直接初始化对象
最后一步通常是优化出来的,转换结果直接在分配给目标对象的内存中构造,但适当的构造函数(移动或复制)即使未使用,也需要可访问。(直到C++17)

请注意,ojbect是从转换后的
A
(从
int
)直接初始化的,复制构造函数标记为
explicit
,这里不重要


顺便说一句:由于C++17的原因,复制构造将被完全省略

在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数有明显的副作用。对象直接构造到存储中,否则它们将被复制/移动到存储中。复制/移动构造函数不必在耳鼻喉科或无障碍:

两者的区别

A a = A(1);

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

如果
T
是一个类类型,而另一个类型的cv非限定版本是
T
或从
T
派生的类,则检查
T
非显式构造函数,并通过重载解析选择最佳匹配。然后调用构造函数初始化对象


这就是为什么它不能与
显式
复制构造函数一起工作。

这是否意味着当我使用像
A A=1;
这样的转换构造时,它直接像
A(A(1))
?@sakugawa是的,在概念上。请注意,对于构造
A(1)
转换构造函数必须是非显式的。但为什么转换构造与VS2017中stdc++14的复制构造函数无关?我设置为
=delete
,但仍然有效。@sakugawa我不确定,MSV可能会在C++17之前的模式下执行强制复制省略,或者在处理复制省略时有错误。在C之前++17,即使执行了复制省略,复制/移动构造函数也需要存在并可访问。我在stdc++17中使用了g++8.1,它确实执行了复制省略,正如您所说的,而c++11和c++14都没有。它可能在MSVC和stdc++14中存在错误。
A a = A(1);
A b = 1;