为什么没有调用复制构造函数

Why is copy constructor not called

本文关键字:复制 构造函数 调用 为什么      更新时间:2023-10-16

这里有一个简单的类头文件和一个主程序。在主程序中,我认为复制构造函数在以下三种情况下被调用:初始化(显式复制)、函数参数的按值传递和函数的按值返回。然而,它似乎没有被要求为其中一个,我认为(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";}
};

Main:

#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 nn";
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

由于复制在C++中频繁发生,而且成本可能很高,因此允许编译器省略某些复制(和移动)构造。即使消隐的构造函数和/或析构函数具有与程序中的输出类似的副作用,这种复制省略也是允许的(也就是说,这并不是真正的优化,因为有复制省略和没有复制省略的行为是不同的)。

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

  1. return语句中,当直接返回与函数返回类型相同类型的局部变量时
  2. throw语句中,当抛出对象时,最里面的try-块中的自动变量的副本可以被忽略
  3. 当一个临时对象没有绑定到引用复制时,它可以被消除
  4. catch子句按值捕获对象并且该对象的类型与throw语句中的对象相同时,可以消除副本

确切的规则稍微复杂一些,但我认为这就是它的要点。考虑到复制省略的规则相当严格,很容易抑制复制省略:最简单的方法是使用identity()函数:

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

(不过,这个版本也禁止move构造;使用T&&推导类型并适当地返回它应该可以进行move构造,但我不太确定返回类型和return语句应该是什么)。

Number 4正在复制一个临时的。它是复制省略的候选者。也就是说,在某些条件下,编译器可以消除对复制构造函数的调用,即使复制构造函数有副作用。