因此,我正在学习有关构造函数初始化器列表的信息,并编写了以下代码:
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 ++编译器。它调用构造函数而不是复制构造函数。它应该正确调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里有什么问题,标准对此有何说明?
答案 0 :(得分:17)
由于copy elision,此处省略了复制构造。从C ++ 17可以保证此行为。在C ++ 17之前不是强制性的;允许编译器执行编译省略操作 1 ,但不是必需的。
在以下情况下,即使复制/移动构造函数和析构函数具有明显的副作用,也要求编译器忽略类对象的复制和移动构造。它们不需要出现或不可访问,因为语言规则可确保即使在概念上也不会进行复制/移动操作:
在初始化中,如果初始化程序表达式是prvalue,并且源类型的cv不合格版本与目标程序的类相同,则使用初始化程序表达式来初始化目标对象:>
T x = T(T(T())); // only one call to default constructor of T, to initialize x
这意味着mango
将直接由默认构造函数初始化。
[1] 实际上,大多数实现也会在C ++ 17之前执行复制省略。使用Gcc,您可以在C ++ 17之前的模式下尝试使用-fno-elide-constructors
选项来禁用复制省略。
答案 1 :(得分:2)
正如Songyuanyao在出色的回答中已经很好地解释了那样,在许多情况下,允许编译器优化对副本构造函数的调用。
还是,我想解决您问题的另一部分,以防万一有些误解:
我为此使用了g ++编译器。 它调用构造函数而不是复制构造函数。它应该正确调用拷贝构造函数,因为我正在创建一个对象来创建另一个对象?这是什么问题,标准对此有何看法?
您的副本构造函数需要提供(const Mango& other)
才能被调用。如果编译器不执行复制删除,则首先会看到对“常规”构造函数"Mango::ctor()"
的调用,然后是对复制构造函数"Mango::copy_ctor()"
的调用。复制省略只是优化了不必要的复制构造函数调用-需要以两种方式调用对象的构造函数,才能在Mango()
中获得Box() : mango(Mango())
对象