考虑以下计划:
class C {
...
};
const C f() {
C ret;
cout << &ret << endl;
return ret;
}
int main() {
C value = f();
cout << &value << endl;
}
result: // note the address are the same
0x7ffdd24b26e0
0x7ffdd24b26e0
函数f()和变量'value'中的变量'ret'具有相同的内存地址,因此看起来'value'不是'ret'的副本。变量'ret'是一个堆栈变量,因此在f()返回后它应该无效。那么为什么c ++允许在函数内返回堆栈值?
g ++版本:
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
答案 0 :(得分:4)
ret和地址相同的原因是所谓的返回值优化(RVO)。这意味着在这种情况下不会执行副本。但请注意,您不能依赖于此,因为它不一定会发生(尽管这会随着C ++ 17 [至少是当前的草案]而改变)。
答案 1 :(得分:3)
这是copy-elision的一个例子,特别是RVO(返回值优化)。它允许您避免返回对象的性能损失。
我和很多不了解RVO的人一起工作,写过像:
void get_stuff(std::vector<int>& foo /*and input params*/) {
// add a whole lot of integers into foo.
}
因为他们认为这比通过以下方式更便宜(避免副本):
void get_stuff(/*input params*/){
std::vector foo;
// populate foo.
return foo;
}
这导致不必要的冗长,并且通常难以阅读代码。这是典型的过早优化 - 你不会犯的错误,因为你现在知道RVO!
答案 2 :(得分:1)
按值返回函数结果是其中一个地方,允许编译器将副本作为优化进行删除,从而将代码转换为道德等同于此:
void f(uint8_t* memory) {
new(memory) C; // create object reserved memory location
cout << (size_t)memory << endl;
}
int main() {
alignas(alignof(C)) uint8_t value[sizeof(C)]; //<- reserve properly aligned raw storage of appropriate size on the stack
f(value);
cout << (size_t)&value[0] << endl;
}
这种优化技术称为NRVO(命名返回值优化),实际上是大多数调用约定的一个非常自然的结果,它指定 - 对于无法通过寄存器返回的值 - 返回的值放在一个地址无论如何都是由调用者指定的。
答案 3 :(得分:0)
我不确定这是否真的是RVO。同样有效的假设是ret
和value
的存储都在实现的堆栈上,并且它们共享相同的堆栈槽,因为它们的生命周期实际上是非重叠的。可能有中间人。