【问题标题】:Is it possible to std::move objects out of functions? (C++11)是否可以将 std::move 对象移出函数? (C++11)
【发布时间】:2012-11-17 02:19:48
【问题描述】:

这个程序试图将一个字符串移出一个函数并用它来构造另一个字符串:

#include <iostream>
#include <string>
#include <utility>

std::string && Get_String(void);

int main(){

    std::string str{Get_String()};
    std::cout << str << std::endl;

    return 0;
}

std::string && Get_String(void){

    std::string str{"hello world"};

    return std::move(str);
}

程序可以编译,但执行时会出现段错误。


这是我的理由:Get_String 将创建一个本地字符串。在字符串超出范围之前,需要制作并返回该字符串的副本。该副本将用于构造 main 中的字符串。但是,如果我将字符串移出函数,则不需要进行复制。

为了理解移动语义,有人可以解释为什么我在做什么,可能没有意义。是否可以将对象移出函数?


编辑:
如果我从以下位置更改函数签名,它将正确编译并运行:

std::string && Get_String(void);

std::string Get_String(void);  

在这种情况下,在返回期间移动字符串是否仍然更有效?

【问题讨论】:

  • RVO 解决了这个问题。不要使用 std::move 禁用它。
  • @chris 我应该按值返回,无论如何编译器都会优化我正在做的事情?
  • 是的,任何现代编译器都应该省略副本。
  • 悬空引用是悬空引用;不管是左值还是右值引用。

标签: c++ function c++11 move-semantics rvalue-reference


【解决方案1】:

鉴于这个例子,

X foo ()
{
  X x;    
  return x;
}

保证以下行为:

• 如果X 具有可访问的副本或移动构造函数,编译器可能 选择省略副本。这就是所谓的(命名的)返回值 优化 ((N)RVO),它甚至在 C++11 之前就已经指定,并且是 大多数编译器都支持。
• 否则,如果X 具有移动构造函数,则移动x
• 否则,如果X 具有复制构造函数,则复制x
• 否则,会发出编译时错误。

另请注意,如果返回的对象是本地非静态对象,则返回右值引用是错误的:

X&& foo ()
{
  X x;
  return x; // ERROR: returns reference to nonexistent object
}

右值引用是一个引用,在引用本地对象时返回它意味着你 返回对不再存在的对象的引用。是否使用std::move() 不 问题。

std::move() 并没有真正移动对象;它只会将左值转换为右值。

【讨论】:

    【解决方案2】:

    Get_String 函数将右值引用绑定到函数局部对象。右值引用对于即将被销毁的东西很有用,但对于已经被销毁的东西,它和左值引用一样糟糕。

    要将本地对象移出函数,只需按类类型返回:

    std::string Get_String(void) {
        std::string str{"hello world"};
        return str;
    }
    

    如果编译器无法完全消除复制/移动,则调用者获取的返回值将使用移动构造函数而不是复制构造函数构造,只要返回表达式为:

    • 临时的,或
    • 单个标识符命名具有自动存储持续时间的事物(如上面的str),或
    • std::move(something)

    (所以你仍然可以让return std::move(str); 明确,但这里没有必要。)

    【讨论】:

      猜你喜欢
      • 2015-11-21
      • 2015-03-21
      • 1970-01-01
      • 2013-07-16
      • 2014-09-30
      • 2023-03-22
      • 2018-07-25
      • 2022-12-23
      相关资源
      最近更新 更多