为什么不调用复制构造函数

时间:2013-12-26 16:23:32

标签: c++ copy-constructor pass-by-value return-by-value

这是一个简单的类头文件和一个主程序。在主程序中,我认为复制构造函数在三种情况下被调用:初始化(显式复制),函数参数的值传递,以及函数的值返回。然而,似乎它没有被其中一个调用,我认为评论中编号为(3)或(4)。它被称为哪个数字(1) - (4)?感谢。

X.h:

#include <iostream>

class X
{
public:
    X() {std::cout << "default constructor \n";}
    X(const X& x) { std::cout << "copy constructor \n";}
};

主:

#include "X.h"

X returnX(X b)  // (1) pass by value - call copy constructor?
{
    X c = b;  // (2) explicit copy - call copy constructor?
    return b;  // (3) return by value - call copy constructor?
}

int main()
{
    X a; // calls default constructor

    std::cout << "calling returnX \n\n";
    X d = returnX(a);  // (4) explicit copy - call copy constructor?
    std::cout << "back in main \n";
}

输出:

default constructor
calling returnX

copy constructor
copy constructor
copy constructor 
back in main

2 个答案:

答案 0 :(得分:7)

由于复制经常在C ++中发生,并且因为它可能很昂贵,所以允许编译器忽略某些复制(和移动)结构。即使被删除的构造函数和/或析构函数具有类似程序输出的副作用,也允许使用此复制省略(也就是说,它不是真正的优化,因为有和没有复制省略的行为不同)。

根据12.8 [class.copy]第31段,有四个可以应用复制省略的基本位置:

  1. return语句中直接返回与函数的返回类型具有相同类型的局部变量。
  2. throw语句中,当抛出对象时,可以省略最内层try - 块内的自动变量的副本。
  3. 当临时对象未绑定到引用复制时,可以省略它。
  4. catch子句按值捕获对象并且类型与throw语句中的对象相同时,可以省略副本。
  5. 确切的规则稍微复杂一点,但我认为这是它的要点。鉴于复制省略的规则相当严格,很容易抑制复制省略:最简单的方法是使用identity()函数:

    template <typename T>
    T const& identity(T const& object) {
        return object;
    }
    ...
    X d = identity(returnX(a));
    

    (这个版本也禁止移动构造;使用T&&推断类型并适当地返回它应该使移动构造成为可能但我不太确定返回类型和返回语句应该是什么)

答案 1 :(得分:6)

4号正在复制临时。它是copy elision的候选人。也就是说,在某些条件下,允许编译器消除对复制构造函数的调用,即使复制构造函数有副作用。