5

C++ 移动语义中似乎有很多错失的机会。我想了解这些背后的基本原理以及为什么标准在定义在以下情况下何时应该移动变量时没有更积极:

string f()
{
    string s;
    return s + " ";
}

这调用operator+(const string&, const char*),不是operator+(string&&, const char*),我相信因为s是一个左值。标准不能说,在函数中最后一次使用局部变量时,该变量被认为是可移动的吗?

我认为一个有点类似的例子:

struct A { A(string&&); };
string g()
{
    string s;
    return s; // s is moved
}
A h()
{
    string s;
    return s; // s can't be moved!
}

g使用 move 语义将数据从s返回值移动,但由于未移入而h无法编译。我相信这是因为标准有一个特殊情况,它本质上说如果你返回一个与返回类型完全相同类型的局部变量,该变量就会被移动。为什么如果返回一个局部变量,不管它的类型如何,它都会被移动?shg

4

2 回答 2

6

我确信这可能需要移动任一示例,但随后有人会提出另一种情况,他们认为s最后一次使用它是“明显的”,因此应该移动。

最终,您将拥有定义编译器需要执行哪些数据流分析的标准。作者决定保守地划清界限,让实现在这方面变得愚蠢。程序员总是可以通过编写std::move将副本更改为移动。

另一种可能性是标准规定是否移动或复制对象是未指定的,前提是代码不再使用它们。这将允许实现尽可能聪明。我很确定这将是一个坏主意:在实践中,用户通常不关心他们的对象是否被移动,但他们有时需要解决这个问题。

于 2012-11-29T16:36:22.153 回答
2

简而言之,这是当前标准中不必要的限制,它使自动移动取决于复制省略的可用性。

另一个例子是:

struct X{
  std::string s;
};

std::string foo(){
  X x;
  return x.s; // no automatic move
};

我在http://isocpp.org的未来标准提案论坛上打开了一个帖子,可以在这里看到。在 Richard Smith 的建议下,我直接向 Mike Miller 发送了关于在此问题上打开一个核心问题的邮件,并得到了以下回复:

[...] 根据上面 Richard 的总结,这听起来确实是一个合理的问题,所以我将在问题列表的下一次修订中为它打开一个问题。谢谢。

因此对于 C++14,所有这些限制都可能会消失,并且每次返回局部变量时,您都会自动移动。

理查德的总结顺便说一句是这样的:

更具体地说,[class.copy]p31 有一条规则,即可以为语句“return id-expression;”省略副本。其中 id-expression 命名了一个局部变量,并且该变量具有与函数的返回类型相同的 cv 非限定类型。建议是,只要有“return id-expression;”语句,我们就应该执行自动移动;其中 id-expression 命名局部变量或函数参数,而不管变量的类型。

于 2012-11-29T16:50:59.230 回答