构造函数初始值设定项列表未调用复制构造函数

时间:2018-06-20 06:33:57

标签: c++ constructor initialization language-lawyer copy-elision

因此,我正在学习有关构造函数初始化器列表的信息,并编写了以下代码:

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 ++编译器。它调用构造函数而不是复制构造函数。它应该正确调用复制构造函数,因为我正在创建一个对象来创建另一个对象?这里有什么问题,标准对此有何说明?

2 个答案:

答案 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())对象