11

可能重复:
如果我返回文字而不是声明的 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. 使用移动语义,它是否比编译器提供的“复制省略”优化效率低?

4

1 回答 1

19
  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();

在这里,将省略两个动作(涉及通常所说的命名返回值优化)。这里有两点需要考虑。首先,return str;满足复制/移动省略的标准(§12.8/31):

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

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

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

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

于 2012-12-23T15:49:55.350 回答