13

该程序尝试将字符串移出函数并将其用于构造另一个字符串:

#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);  

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

4

2 回答 2

19

鉴于这个例子,

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()并没有真正移动物体;它只会将左值转换为右值。

于 2012-11-29T03:57:25.803 回答
3

Get_String函数将右值引用绑定到函数本地对象。右值引用对于即将被销毁的事物很有用,但对于已经被销毁的事物,它与左值引用一样糟糕。

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

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

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

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

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

于 2012-11-29T03:58:04.570 回答