可以将std::string进行自我移动吗?

5
std::string s = "y";
s = "x" + std::move(s) + "x";
Send(std::move(s));

微软的STL实现会检查这一点,但标准要求吗?

相对于插入+追加或使用两个变量的方法,这看起来更加简洁。

注意:我知道我可以使用Send(std::move("x" + std::move(s) + "x")),但实际代码并不简单。


3
在语句 "x" + std::move(s) + "x" 中,std::move 的目的是什么? - Some programmer dude
3
过早优化。由于小字符串优化,您很可能根本没有任何东西需要使用std::move。除非有确凿证据表明性能成为问题,否则应选择可读性优化。我还会争辩说,这种方式在任何情况下都不会“看起来更清晰”。 - DevSolar
3
它看起来比插入+追加或两个变量的方法更清晰。”并不是这样。你必须考虑这段代码是否合理,这个事实已经告诉你它并不清晰。对于清晰的代码,你不需要思考它的合理性。 - Nicol Bolas
1
我知道我可以使用std::move("x" + std::move(s) + "x")。这里两次使用std::move都是过早的悲观优化,而且是不必要的,因为中间结果都是临时的。 - Eljay
1
@Eljay,使用move时,第一个调用的是operator+(const char*, string&&)而不是operator+(const char*, const string&) - OwnageIsMagic
显示剩余7条评论
1个回答

16

这里没有自己移动的操作。自己移动是指像这样的东西:

s = std::move(s);

这意味着

s.operator=(std::move(s));
换句话说,当调用operator=时,this指向与参数相同的字符串。 在您的代码中,"x" + std::move(s)将首先被评估,它返回一个std::string prvalue。换句话说,并不是对s的引用,尽管连接可能会从s中“窃取”缓冲区。然后评估第二个+,并返回另一个prvalue。最后,调用operator=。此时,右侧的prvalue必须具体化,因为它绑定到operator=的rvalue引用参数。因此,我们只有s从临时对象被赋值的情况,其中可能已经从s中窃取了缓冲区。但无论如何:s是一个标准库类型的对象,这意味着它处于“有效但未指定状态”。由于s处于有效状态,并且与RHS不是同一对象,因此没有问题。

2
@OwnageIsMagic operator+ 不会返回其参数的 rvalue reference,而是返回一个 prvalue,该 prvalue 在您所查看的具体实现中由其 rvalue reference 参数初始化。请注意返回类型不是 rvalue reference。 - Brian Bi
@alfC v = std::move(v);v=v; 都是无操作语句。这是确保自我赋值不会引起任何问题的基础之一。通常情况下,它不需要是无操作语句,但我还没有找到一个例子证明它不是(除非出于某些原因进行了一些副作用)。 - ALX23z
@ALX23z,v = std::move(v) 对于某些(大多数?)实现来说会清除向量。我认为这是自然结果,当你考虑“性能”时(最小化操作和检查的数量)。而且这不是“int”的行为©。 - alfC
这个困境有一个对应的问题,即“当一元运算符+存在时应该返回什么?”。到目前为止,我的结论是,即使它可以利用移动,它也应该始终返回一个副本。这样,即使对于特定情况不是最优的,s = +std::move(s) 总是定义良好的。顺便提一下,“std::identity”应该返回什么? - alfC
1
@alfC • 同意。Howard Hinnant(在Stack Overflow上)指出,开发人员的自己的类可以为移动后的对象设置任何后置条件。标准C ++库是使用有效但未指定习语的库。我的代码也倾向于遵循这个规则,并添加了一些仅在调试构建中才能检测到错误的代码。 - Eljay
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接