【问题标题】:understanding c++11 rvalues, move semantics and performance [duplicate]理解c ++ 11右值,移动语义和性能[重复]
【发布时间】:2012-12-10 08:13:04
【问题描述】:

可能重复:
What happens if I return literal instead of declared std::string?

考虑下面的代码

string getName () {
    return "meme";
}

string name = getName();

函数getName() 返回一个临时对象。在 C++03 中,我理解 string 的复制构造函数被调用并且临时对象被销毁。实际上,编译器(至少在 GCC 4.7 中)似乎通过不创建对象 name 而是将其替换为临时对象本身而不破坏临时对象来优化第 5 行。 (我尝试使用 MyVector 类,而不是 std::string)

按照 C++11 标准中的定义,

  1. getName() 是否返回右值?

  2. 在上面的第 5 行中,调用了哪个字符串的构造函数(移动或复制)?我是否必须调用 std::move() 才能调用移动构造函数?

  3. 使用移动语义,是不是比编译器提供的“复制省略”优化效率低?

【问题讨论】:

  • 那将是 复制省略...
  • @K-ballo 感谢您启发我。
  • 添加行号看起来很棒!以前从未见过=)
  • @Coodey:我同意,如果它没有搞砸复制/粘贴。 :P 对我来说,这看起来像是一些我必须编辑出来以获得可编译示例的东西。

标签: c++ c++11 move-semantics copy-elision return-value-optimization


【解决方案1】:
  1. 函数不返回右值或左值。值类别适用于表达式。因此,调用函数的表达式可能是右值或左值。在这种情况下,表达式getName() 是一个右值表达式,因为函数getName 按值返回一个对象。这来自§5.2.2/10:

    如果结果类型是左值引用类型或对函数类型的右值引用,则函数调用是左值,如果结果类型是对对象类型的右值引用,则为 xvalue,否则为纯右值.

    您的函数结果类型不是左值或右值引用,因此函数调用是纯右值。 prvalue 表达式是 rvalue 表达式的子集。

  2. 将使用移动构造函数(除非它被省略,它可能是)。那是因为getName() 是一个右值,所以采用右值引用的std::string 的构造函数将更好地匹配参数。请注意,即使省略了移动构造,移动构造函数仍必须可访问。也就是说,即使没有省略,代码也必须是可编译的。

  3. 一般来说,复制或移动省略的优化会完全摆脱任何复制或移动。因此,它当然比实际移动要快。如果一个动作被省略,实际上什么都不会发生。该动作不会发出任何代码。编译器通过直接在对象将被复制或移动到的位置构造对象来实现这一点。

值得一提的是,这也可以进行等效优化:

string getName () {
  std::string str("meme");
  return str;
}
 
string name = getName();

在这里,两个动作将被省略(涉及通常所说的Named Return Value Optimization)。这里有两点需要考虑。首先,return str; 符合复制/移动省略的标准(第 12.8/31 节):

这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):

  • 在具有类返回类型的函数的 return 语句中,当表达式是与函数具有相同 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时返回类型,直接在函数的返回值中构造自动对象即可省略复制/移动操作
  • ...

其次,虽然str 是一个左值,但它仍然会被移出,因为它符合标准(§12.8/32)给出的特殊情况:

当满足或将满足省略复制操作的条件时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,重载决策选择构造函数首先执行复制,就好像对象是由右值指定的一样。

【讨论】:

    猜你喜欢
    • 2011-06-26
    • 2013-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-19
    相关资源
    最近更新 更多