C++移动语义澄清

4
我有一个包含std::string的Text类。有一个名为SetText的方法,如下所示:
void Text::SetText( const std::string& str )
{
    m_str = str;
}

自从这个方法几乎总是被用作参数的右值,我考虑使用移动构造函数。我了解基本原理,但只限于此。因此我进行了测试,并得出结论:另一个类似方法会更好,一旦定义了移动构造函数和移动赋值操作符,就可以获得性能上的提升:
void Text::SetText( std::string&& str )
{
    m_str = move( str );
}

以下是我的问题:

  • 它是否适用于std容器?它们提供移动构造函数和赋值吗?
  • 当类中没有堆分配(我指的是完全没有堆分配,因此没有智能指针作为类成员)时,移动语义是否有用?

谢谢。

1个回答

7

你的代码是正确的。但是首选的C++11方法如下:

void Text::SetText(std::string str)
{
    m_str = move(str);
}

那就是说,按值传递并从中移动。如果您传递rvalue,则会执行两个移动操作;如果您传递lvalue,则会执行一个复制和一个移动操作,其中后一种情况需要进行一次复制。
“标准库”为所有类提供了移动构造函数和移动赋值函数,其中可以比复制更有效地使用它们。
除了其他类型的资源外,如果类中没有堆分配(我指根本没有堆分配,因此没有智能指针作为类成员),则移动语义很有用。
如果对象的所有数据都直接存储在对象中,则必须对它们进行复制,并且没有其他工作可以通过移动来保存。
另请注意,如果将函数设置为内联,则编译器应该能够省略rvalue情况下的一个移动操作。除非也将“operator=”设置为内联,否则它将无法省略lvalue情况下的移动操作,因为它不允许将移动赋值替换为复制赋值。

1
你为什么不使用 std::swap(m_str, str) 呢? - WhozCraig
提供一个 void Text::SetText( std::string&& str ) 的重载也是正确的。 - TNA
1
@TNA:是的,但你需要同时拥有 std::string &&std::string const & 两种重载,而且得到的好处并不多。 - Jan Hudec
1
@Aulaulz:如果你传递一个左值,那么就会有一个不可避免的副本。你可以在参数中进行操作(使用传值方式,需要额外的移动),也可以在赋值中进行操作(使用引用方式,需要两个重载),但是你无法避免它。 - Jan Hudec
@WhozCraig: 当实现对象的operator=时,使用std::swap。但是如果实现一个setter,使用std::swap不会更好,而在极少数情况下可能比移动赋值运算符更糟糕。 - Jan Hudec
显示剩余4条评论

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