Possible Duplicate:
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 标准中所定义,
getName()
是否返回右值?
在上面的第 5 行中,调用了字符串的哪个构造函数( move 或复制)?我是否必须调用 std::move()
来调用 move 构造函数?
使用 move 语义,它的效率是否低于编译器提供的“复制省略”优化?
函数不返回 右值或左值。值类别适用于表达式。因此,调用 函数的表达式可能是右值或左值。在这种情况下,表达式 getName()
是右值表达式,因为函数 getName
按值返回对象。这来自 §5.2.2/10:
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
您的函数结果类型不是左值或右值引用,因此函数调用是纯右值。纯右值表达式是右值表达式的子集。
将使用 move 构造函数(除非它被省略,它可能是)。这是因为 getName()
是一个右值,所以采用右值引用的 std::string
的构造函数将更好地匹配参数。请注意,即使 move 构造被省略, move 构造仍然必须是可访问的。也就是说,即使没有省略,代码也必须是可编译的。
一般来说,复制或 move 省略的优化将完全摆脱任何复制或 move 。所以当然它比实际 move 要快。如果省略了一步,实际上什么也不会发生。该 move 不会发出任何代码。编译器通过直接在对象将被复制或 move 到的位置构造对象来实现这一点。
值得一提的是,这也可以等效优化:
string getName () {
std::string str("meme");
return str;
}
string name = getName();
此处将省略两步(即通常所说的Named Return Value Optimization)。这里有两点需要考虑。首先,return str;
满足复制/move 省略的标准 (§12.8/31):
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
- ...
其次,虽然 str
是一个左值,但它仍然会被 move ,因为它符合标准 (§12.8/32) 给出的特殊情况:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.