将参数省略复制为 return 值
Copy elision of arguments as return values
- 鉴于以下情况; C++17 会保证复制省略吗?
template <typename Widget>
Widget frobnicate(Widget w) {
// optionally mutate w in some way
return w;
}
如果 Widget
实现移动构造函数,答案会改变吗?
我应该 return 搬家吗?例如,在这种情况下:
return std::move(w);
没有。我们不能保证从函数参数中复制省略。如果您从实现的角度考虑这一点 - 您的参数 w
必须存在于某个寄存器或地址中,因此它不能首先构造到 return 槽中。
鉴于 (1),有点无意义。
你永远不应该†‡写return std::move(w);
因为这实际上会抑制在快乐情况下的复制省略:
Widget frobnicate() {
Widget w;
return std::move(w); // oops
}
†除了在 C++17 中实际上有一些不幸的地方你实际上需要写 std::move
因为有些地方对象不会自动为你搬离。有关几个示例,请参阅 P0527 and P1155(OP 案例 - returning 一个函数参数 - 不是其中之一;甚至在 C++17 中也会隐式移动该案例)。 C++20 也会隐含地从所有这些情况中转移,所以你真的不应该‡写return std::move(w);
‡当然可以,除非w
不是一个函数参数或局部变量自动存储时间。然后语言中没有任何内容可以安全地假定它可以代表您移动它,因此您必须明确地这样做。
复制省略只会发生在方法中实例化的变量上。 Thar是由于如何复制省略。调用者在调用被调用者时会将 space 设为 return 值。但是为了使用那个 space,被调用者必须在它的定义中使用这个 space 创建一个变量(理论上它可能可以直接在参数中制作的副本(因为它是由值)到这个 space vut 编译器还不是那么好)) 资料来源:关于复制省略的 cppcon 讨论。
移动构造函数的存在不会给你复制省略,但如果复制省略是不可能的,编译器将首先尝试移动,如果不可能移动,然后再复制。所以如果没有复制省略,移动构造函数的存在可能会提高速度。
你永远不应该 return std::move
临时变量(即在函数末尾超出范围的变量),因为它可以防止复制省略,即使复制省略是不可能的,编译器将默认移动。 mkve return 的唯一原因(我能想到)是,如果您要释放调用前对象持有的资源。例如 std::unique_ptr::release
应该 return by move, iirc.
- 鉴于以下情况; C++17 会保证复制省略吗?
template <typename Widget>
Widget frobnicate(Widget w) {
// optionally mutate w in some way
return w;
}
如果
Widget
实现移动构造函数,答案会改变吗?我应该 return 搬家吗?例如,在这种情况下:
return std::move(w);
没有。我们不能保证从函数参数中复制省略。如果您从实现的角度考虑这一点 - 您的参数
w
必须存在于某个寄存器或地址中,因此它不能首先构造到 return 槽中。鉴于 (1),有点无意义。
你永远不应该†‡写
return std::move(w);
因为这实际上会抑制在快乐情况下的复制省略:Widget frobnicate() { Widget w; return std::move(w); // oops }
†除了在 C++17 中实际上有一些不幸的地方你实际上需要写 std::move
因为有些地方对象不会自动为你搬离。有关几个示例,请参阅 P0527 and P1155(OP 案例 - returning 一个函数参数 - 不是其中之一;甚至在 C++17 中也会隐式移动该案例)。 C++20 也会隐含地从所有这些情况中转移,所以你真的不应该‡写return std::move(w);
‡当然可以,除非w
不是一个函数参数或局部变量自动存储时间。然后语言中没有任何内容可以安全地假定它可以代表您移动它,因此您必须明确地这样做。
复制省略只会发生在方法中实例化的变量上。 Thar是由于如何复制省略。调用者在调用被调用者时会将 space 设为 return 值。但是为了使用那个 space,被调用者必须在它的定义中使用这个 space 创建一个变量(理论上它可能可以直接在参数中制作的副本(因为它是由值)到这个 space vut 编译器还不是那么好)) 资料来源:关于复制省略的 cppcon 讨论。
移动构造函数的存在不会给你复制省略,但如果复制省略是不可能的,编译器将首先尝试移动,如果不可能移动,然后再复制。所以如果没有复制省略,移动构造函数的存在可能会提高速度。
你永远不应该 return std::move
临时变量(即在函数末尾超出范围的变量),因为它可以防止复制省略,即使复制省略是不可能的,编译器将默认移动。 mkve return 的唯一原因(我能想到)是,如果您要释放调用前对象持有的资源。例如 std::unique_ptr::release
应该 return by move, iirc.