C++ 复制初始化和直接初始化之间有区别吗?
假设我有这个函数:C++ 复制初始化和直接初始化之间有区别吗?,c++,initialization,C++,Initialization,假设我有这个函数: void my_test() { A a1 = A_factory_func(); A a2(A_factory_func()); double b1 = 0.5; double b2(0.5); A c1; A c2 = A(); A c3(A()); } 在每个分组中,这些语句是否相同?或者在某些初始化中是否有额外的(可能是可优化的)副本 我见过有人说这两件事。请引用文本作为证据。也请添加其他案例。作业不同于初
void my_test()
{
A a1 = A_factory_func();
A a2(A_factory_func());
double b1 = 0.5;
double b2(0.5);
A c1;
A c2 = A();
A c3(A());
}
在每个分组中,这些语句是否相同?或者在某些初始化中是否有额外的(可能是可优化的)副本
我见过有人说这两件事。请引用文本作为证据。也请添加其他案例。作业不同于初始化 以下两行都进行初始化。完成单个构造函数调用:
A a1 = A_factory_func(); // calls copy constructor
A a1(A_factory_func()); // calls copy constructor
但这并不等于:
A a1; // calls default constructor
a1 = A_factory_func(); // (assignment) calls operator =
我目前没有文本来证明这一点,但很容易进行实验:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "default constructor" << endl;
}
A(const A& x) {
cout << "copy constructor" << endl;
}
const A& operator = (const A& x) {
cout << "operator =" << endl;
return *this;
}
};
int main() {
A a; // default constructor
A b(a); // copy constructor
A c = a; // copy constructor
c = b; // operator =
return 0;
}
#包括
使用名称空间std;
甲级{
公众:
A(){
cout这些情况中有很多都取决于对象的实现,因此很难给出具体的答案
考虑一下这个案例
A a = 5;
A a(5);
在这种情况下,假设一个合适的赋值运算符&初始化接受单个整数参数的构造函数,我如何实现所述方法会影响每一行的行为。然而,通常的做法是,其中一个在实现中调用另一个,以消除重复的代码(尽管在如此简单的情况下,没有真正的目的。)
<>编辑:如其他响应中提到的,第一行实际上将调用复制构造函数。将与赋值运算符相关的注释视为与单独赋值有关的行为。
也就是说,编译器如何优化代码将有它自己的影响。如果我让初始化构造函数调用“=”运算符,如果编译器没有进行优化,那么顶行将执行2次跳转,而不是底行中的一次跳转
现在,对于最常见的情况,您的编译器将在这些情况下进行优化,并消除此类低效。因此,您所描述的所有不同情况实际上都是相同的。如果您想确切了解正在执行的操作,可以查看编译器的目标代码或程序集输出。doubleb1=0.5;
是构造函数的隐式调用
双b2(0.5);
是显式调用
查看以下代码以查看差异:
#include <iostream>
class sss {
public:
explicit sss( int )
{
std::cout << "int" << std::endl;
};
sss( double )
{
std::cout << "double" << std::endl;
};
};
int main()
{
sss ddd( 7 ); // calls int constructor
sss xxx = 7; // calls double constructor
return 0;
}
#包括
类sss{
公众:
显式sss(int)
{
std::cout第一个分组:它取决于A_factory_func
返回的内容。第一行是复制初始化的示例,第二行是直接初始化。如果A_factory_func
返回一个A
对象,那么它们是等价的,它们都调用A
的复制构造函数,否则第一个版本对于返回类型为A\u factory\u func
或相应的A
构造函数,sion从可用的转换运算符创建类型为A
的右值,然后调用复制构造函数从该临时构造函数构造a1
。第二个版本尝试找到一个合适的构造函数,该构造函数接受的任何内容e> _factory_func
返回,或获取返回值可以隐式转换为的内容
第二个分组:逻辑完全相同,只是内置类型没有任何外来构造函数,所以实际上它们是相同的
第三组:c1
默认初始化,c2
从临时初始化的值复制初始化。如果用户提供了默认构造函数(如果有的话),则具有pod类型的c1
的任何成员(或成员的成员等)都可能不会初始化不要显式初始化它们。对于c2
,这取决于是否有用户提供的复制构造函数,以及该构造函数是否适当地初始化了这些成员,但是临时的成员都将被初始化(如果没有显式初始化,则初始化为零).正如litb所指出的,c3
是一个陷阱。它实际上是一个函数声明。C++17更新
在C++17中,A_factory_func()
的含义从创建临时对象(C++注意:
[12.2/1]类类型的临时变量是在不同的上下文中创建的:……以及在某些初始化(8.5)中创建的。
即,用于复制初始化
[12.8/15]当满足某些条件时,允许实现省略类对象的复制构造…
换句话说,一个好的编译器不会在可以避免的情况下为副本初始化创建副本;相反,它只会直接调用构造函数——也就是说,就像直接初始化一样
换句话说,在大多数情况下,复制初始化就像直接初始化一样,因为直接初始化可能会导致任意(因此可能未知)转换,所以我更喜欢在可能的情况下始终使用复制初始化。(还有一点是,它实际上看起来像是初始化。)
技术血腥:
[12.2/1同上]即使避免创建临时对象(12.8),也必须遵守所有语义限制,就像临时对象是创建的一样。
<>高兴的是我没有编写C++编译器。 关于这一部分的回答:
c2=A();c3(A())
由于大多数答案都是c++11之前的版本,我要补充c++11对此的看法:
简单类型说明符(7.1.6.2)或类型名说明符(14.6)
后跟括号中的表达式列表构造
给定表达式列表的指定类型。如果表达式列表是
单个表达式,类型转换表达式是等效的(在
定义,以及(如果定义有意义)对应的类型
表达式(5.4)。如果指定的类型是类类型,则类
类型应完整。如果表达式列表指定了多个
单一值,类型应为具有适当声明的
建造商(8.5、12.1)和t
A a1 = A_factory_func();
A a2(A_factory_func());
double b1 = 0.5;
double b2(0.5);
A c1;
A c2 = A();
A c3(A());
T t(x);
T t = x;
#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&)
B(A const&)
operator B(A&);
class A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
};
class B
{
explicit B(int) { }
explicit B(int, int) { }
};
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
}
A a1 = 1; // this is copy initialization
A a2(2); // this is direct initialization
B b2(2); // this is direct initialization
B b5 = (B)1; // not problem if you either use of assign to initialize and cast it as static_cast