编译器前端的主要工作是从所有内容中删除名称以解析底层语义结构。
避免使用名称确实有助于避免不必要地获取对象的地址,这可能会不经意地阻止编译器操作数据。但是有足够的方法来获取临时地址,这几乎没有实际意义。命名对象的特殊之处在于它们不符合 C++ 中的构造函数省略的条件,但正如您所提到的,移动语义消除了最昂贵的不必要的复制构造。
只专注于编写可读的代码。
您的第一个示例确实消除n
了DoSomething( std::move( n ) )
.
在示例s1 + s2 + s3
中,C++11 确实使事情变得更高效,但移动语义和消除临时性是不同的事情。移动构造函数只是使临时构造更便宜。
我也误以为 C++11 会消除临时性,只要你使用成语
// What you should use in C++03
foo operator + ( foo lhs, foo const & rhs ) { return lhs += rhs; }
这实际上是不真实的;lhs
是命名对象,不是临时对象,不符合复制省略的返回值优化形式。事实上,在 C++11 中,这将产生一个副本,而不是移动!您需要使用std::move( lhs += rhs );
.
// What you should use in C++11
foo operator + ( foo lhs, foo const & rhs ) { return std::move( lhs += rhs ); }
您的示例使用std::string
, not foo
,并且operator+
定义为(本质上,并且自 C++03 起)为
// What the C++03 Standard Library uses
string operator + ( string const & lhs, string const & rhs )
{ return string( lhs ) += rhs; } // Returns rvalue expression, as if moved.
此策略与上述策略具有相似的属性,因为临时对象一旦绑定到引用,就无法进行复制省略。有两个潜在的修复方法,可以在速度和安全性之间进行选择。这两个修复都与第一个习惯用法不兼容,后者move
已经实现了安全样式(因此是您应该使用的!)。
安全的风格。
这里没有命名对象,但是lhs
不能直接将绑定到参数的临时构造到绑定到引用的结果中,从而停止复制省略。
// What the C++11 Standard Library uses (in addition to the C++03 library style)
foo operator + ( foo && lhs, foo const & rhs )
{ return std::move( lhs += rhs ); }
不安全的风格。
接受右值引用并返回相同引用的第二个重载完全消除了中间临时(不依赖省略),允许将调用链+
完美地转换为+=
调用。但不幸的是,它还通过将调用链开头的剩余临时绑定到引用来取消其生命周期扩展的资格。所以返回的引用在分号之前是有效的,但是它会消失,没有什么可以阻止它。所以这主要在模板表达式库之类的东西中很有用,对哪些结果可以绑定到本地引用有记录的限制。
// No temporary, but don't bind this result to a local!
foo && operator + ( foo && lhs, foo const & rhs )
{ return std::move( lhs += rhs ); }
像这样评估图书馆文档需要对图书馆作者的技能进行一些评估。如果他们说要以某种古怪的方式做事,因为它总是更高效,请保持怀疑,因为 C++ 并不是故意设计为古怪的,而是设计为高效的。
然而,在表达式模板的情况下,临时变量包括复杂的类型计算,这些计算会被分配给具体类型的命名变量而中断,你绝对应该听听作者所说的话。在这种情况下,他们可能会更有知识。