return-by-value 中的 C++ 复制构造函数激活

C++ copy constructor activation in return-by-vlaue

我无法找到以下问题的具体答案:

考虑以下代码:

Obj f() {
    Obj o2;
    return o2;
}

int main() {
    Obj o1 = f();
    return 0;
}

复制构造函数被激活了多少次没有编译器优化?

万一没有移动构造函数,是不是一次复制o2到调用函数,另一次构造o1?

万一 move构造函数,是不是一次复制o2到调用函数,另一次构造o1(第二次是move const)?

在 C++17 之前,是的,有两个复制构造函数调用(即使它们有副作用也可以省略)。您可以在 gcc/clang.

中使用 -fno-elide-constructors 查看此内容

自 C++17 以来,由于新的临时物化规则,f 中只涉及一个副本(同样可以省略)。


准确来说都是走法,不是抄袭。

C++03 及之前版本

Obj被复制了两次。一次通过return语句(构造return值),一次通过复制return值来初始化o1

C++11 和 C++14

如果Obj有一个可用的移动构造函数,它被移动两次,复制零次。 return 语句必须使用移动,即使 returned 表达式是左值。由于语言中的特殊规则,这种“移动优化”是强制性的; o2 不得复制,即使禁用了优化。初始化 o1 时会发生第二步。

如果Obj没有移动构造函数或隐式删除移动构造函数,则复制构造函数被使用两次。

如果 Obj 具有显式删除的移动构造函数,则程序格式错误,因为 o1 的初始化尝试使用已删除的移动构造函数。

C++17 及更高版本

如果Obj 有可用的移动构造函数,则在执行return 语句时移动一次。如上所述,编译器必须使用移动而不是复制。 o1 的构造既不涉及复制 也不涉及 移动。相反,f() 中的 return 语句初始化 o1,而不涉及临时变量。这是因为“保证复制省略”:即使禁用优化,该语言也需要省略副本。这是因为 f() 是纯右值,纯右值不会具体化( 实例化为临时对象)除非有必要这样做。该标准创建的“法律虚构”是 f() 实际上 return 是创建 Obj 的“配方”,而不是 Obj 本身。在实践中,这可以像在标准的早期版本中实现(可选)return 值优化的方式一样实现:调用者将指向 o1 的指针直接传递到 freturn 语句将 Obj 构造到此指针中。

如果Obj的移动构造函数被隐式删除或不存在,则return语句将使用复制构造函数,因此将有一个副本零移动。

如果显式删除 Obj 的移动构造函数,则程序的格式错误,如 C++11/C++14 的情况。

在所有情况下

上述情况下的copies/moves可以优化掉。在涉及多个 copy/move 操作的情况下,编译器可以优化其中任何一个或所有操作。