【问题标题】:Casting to rvalue reference to "force" a move in a return value - clarification转换为右值引用以“强制”在返回值中移动 - 澄清
【发布时间】:2025-12-06 22:30:01
【问题描述】:

好的,我开始了解右值引用的要点(我认为)。我有我正在编写的这段代码 sn-p:

#include <iostream>

using namespace std;

std::string get_string()
{
  std::string str{"here is your string\n"};
  return std::move(str);  // <----- cast here?
}

int main ()
{
  std::string my_string = std::move(get_string()); // <----- or cast here?
  std::cout << my_string;
  return 0;
}

所以我有一个简单的例子,我有一个返回字符串副本的函数。我已经读过它不好(并且得到了核心转储来证明这一点!)返回对本地临时变量的任何引用,所以我没有尝试这样做。

main() 的分配中,我不想复制构造该字符串,我想移动构造/分配字符串以避免复制过多的字符串。

  • Q1:我在get_string() 中返回了临时变量的“副本” - 但我已将返回值转换为右值红色。那是没有意义的,还是在做任何有用的事情?

  • Q2:假设 Q1 的答案是我不需要这样做。然后我是将临时变量的新副本移动到my_string,还是直接将临时变量str 移动到my_string

  • Q3:为了将字符串返回值存储到“外部”(在我的情况下是 main())变量中,您需要的最小副本数是多少,您如何做到这一点(如果我还没有实现)?

【问题讨论】:

    标签: c++11 rvalue-reference stdmove


    【解决方案1】:

    我在 get_string() 中返回了临时变量的“副本”——但我已将返回值转换为 rvalue-red。那是没有意义的还是这样做有用的?

    在这种情况下,您不必使用std::move,因为值返回的局部变量会为您“隐式移动”。标准中有一个特殊的规则。在这种情况下,您的举动是悲观,因为它可以防止 RVO(clang 警告)。


    Q2:假设 Q1 的答案是我不需要这样做。那么我是将临时变量的新副本移动到 my_string 中,还是直接将临时变量 str 移动到 my_string 中。

    您不需要std::move 调用get_string() 的结果。 get_string() 是一个prvalue,这意味着my_string 的移动构造函数将被自动调用(C++17 之前)。在 C++17 及更高版本中,强制复制省略 将确保不会发生移动/复制(使用 prvalues)。


    Q3:为了将字符串返回值存储到“外部”(在我的情况下是 main())变量中,您需要的最小副本数是多少,以及如何做到这一点(如果我是尚未实现)?

    取决于标准以及 RVO 是否发生。如果发生 RVO,您将拥有 0 个副本和 0 个移动。如果您以 C++17 为目标并从 prvalue 进行初始化,则保证有 0 个副本和 0 个移动。如果两者都没有发生,您可能只有一个动作 - 我不明白为什么这里应该出现任何副本。

    【讨论】:

    • 谢谢,这是一个不错的答案 +1。只有一个问题 - 为什么它是 0 步?,是因为它是一个纯右值(指向右值的指针?)?
    • "prvalue" 与指针没有任何关系,它代表“纯右值”。它基本上是这样诞生的右值。谷歌“prvalue”和“强制复制省略”。
    【解决方案2】:

    您不需要在返回值上使用std::move,它是一个局部变量。 The compiler does that for you:

    如果表达式是一个左值表达式,它是在主体中声明的自动存储持续时间对象的(可能是带括号的)名称,或者作为最内层封闭函数或 lambda 表达式的参数,则重载决策以选择要用于的构造函数返回值的初始化会执行两次:第一次好像表达式是一个右值表达式(因此它可能会选择移动构造函数),如果没有合适的转换可用,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),第二次执行重载决策,表达式被视为左值(因此它可以选择复制构造函数引用非 const)。

    【讨论】:

    • 谢谢,我想我明白了 - 我真的很难阅读“标准谈话”:o。我认为这是说如果返回右值它不会进行第二次初始化,但如果它返回右值则它会这样做
    • @code_fodder 通过练习,您将更好地阅读标准演讲。不幸的是,别无他法。