【问题标题】:Constructor initializer list is not calling copy constructor [duplicate]构造函数初始值设定项列表未调用复制构造函数[重复]
【发布时间】:2018-11-29 04:51:33
【问题描述】:
所以我正在学习构造函数初始化列表,我写了以下代码:
class Mango
{
public:
Mango(){cout<<"Mango::ctor()";}
Mango(const Mango& other){cout<<"Mango::copy_ctor()";}
};
class Box
{
public:
Box() : mango(Mango()) //**doesn't call copy constructor**
{
}
Mango mango;
};
int main()
{
Box box;
return 0;
}
我为此使用了 g++ 编译器。它调用构造函数而不是复制构造函数。它应该正确调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里有什么问题,标准对此有何规定?
【问题讨论】:
标签:
c++
constructor
initialization
language-lawyer
copy-elision
【解决方案1】:
由于copy elision,这里省略了复制构造。从 C++17 保证了这种行为。在 C++17 之前,它不是强制性的;允许编译器执行复制省略1.
在以下情况下,即使复制/移动构造函数和析构函数具有可观察到的副作用,编译器也必须省略类对象的复制和移动构造。它们不需要存在或可访问,因为语言规则确保不会发生复制/移动操作,即使在概念上也是如此:
-
在初始化时,如果初始化表达式是纯右值,并且源类型的 cv 非限定版本与目标的类是同一类,则使用初始化表达式初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
也就是说mango会直接被默认构造函数初始化。
[1] 事实上,大多数实现在 C++17 之前也会执行复制省略。使用Gcc,您可以尝试在 pre-C++17 模式下使用-fno-elide-constructors 选项来禁用复制省略。
【解决方案2】:
正如宋元耀在他的出色回答中已经很好地解释的那样,编译器可以在许多情况下优化掉复制构造函数的调用。
不过,我想解决你问题的不同部分,以防万一有一些误解:
我为此使用了 g++ 编译器。 它调用构造函数而不是复制构造函数。它应该正确调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里有什么问题,标准对此有何规定?
您的复制构造函数需要提供(const Mango& other) 才能被调用。如果编译器不执行复制省略,您将首先看到对“普通”构造函数"Mango::ctor()" 的调用,然后是对复制构造函数"Mango::copy_ctor()" 的调用。复制省略只是优化了不必要的复制构造函数调用——对象的构造函数需要以任何一种方式调用才能在Box() : mango(Mango()) 中获取Mango() 对象