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
的指针直接传递到 f
, return
语句将 Obj
构造到此指针中。
如果Obj
的移动构造函数被隐式删除或不存在,则return
语句将使用复制构造函数,因此将有一个副本零移动。
如果显式删除 Obj
的移动构造函数,则程序的格式错误,如 C++11/C++14 的情况。
在所有情况下
上述情况下的copies/moves可以优化掉。在涉及多个 copy/move 操作的情况下,编译器可以优化其中任何一个或所有操作。
我无法找到以下问题的具体答案:
考虑以下代码:
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
的指针直接传递到 f
, return
语句将 Obj
构造到此指针中。
如果Obj
的移动构造函数被隐式删除或不存在,则return
语句将使用复制构造函数,因此将有一个副本零移动。
如果显式删除 Obj
的移动构造函数,则程序的格式错误,如 C++11/C++14 的情况。
在所有情况下
上述情况下的copies/moves可以优化掉。在涉及多个 copy/move 操作的情况下,编译器可以优化其中任何一个或所有操作。