std::vector<float> someOp(void)
{
using namespace std;
vector<float> results;
// some operations done to results
return results;
}
int main(void)
{
using namespace std;
vector<float> &results = someOp();
}
someOp返回的向量是否存在于someOp()堆栈空间或main()堆栈空间中?
我倾向于认为它没有被复制/移动到main()堆栈空间,因为结果向量在两个方法中都有相同的地址。
答案 0 :(得分:5)
这两个都不是有效的C ++(并且不会被g ++编译)。
您似乎正在尝试存储对返回的results
的引用,但这是不可能的,因为返回的results
存在于someOp
的堆栈框架中,而且它会在someOp()
返回后仍然存在,将来会在某个时候被覆盖。
答案 1 :(得分:3)
比这复杂一点。
是的,它最初位于someOp
的堆栈空间中。但是,由于您按值返回,因此会复制一份副本。所以它还没有丢失(还)。
但是,当您将其存储到vector<float> &results
时,会存储对它的引用。声明结束后变为无效。返回的向量是语句结束后销毁的中间体。
所以最终的结果是vector<float> &results
变成了一个悬垂的“指针”。
编辑:(见评论)
显然,代码根本不应该编译。但它确实在VS2010中。所以我的回答只适用于它编译的情况。
答案 2 :(得分:2)
这不能编译 - 在标准C ++中,您不能将临时(在这种情况下由std::vector<float>
返回的someOp
)绑定到非const引用。
答案 3 :(得分:0)
问题中的代码是错误的,应该被拒绝,但问题仍然适用于稍微修改它,但在讨论它之前,让我们描述以下更简单的程序:
type f() {
type x; // [1]
return x; // [2]
}
int main() {
type m = f(); // [3]
m.const_function();
}
在上面的程序中,概念上有3个对象,x
内有f
,返回的值未命名,最后m
位于main
内。在[1]中创建x
,然后将其复制到[2]中的返回对象,最终用于复制[3]中的构造m
。
现在回到参考案例:
type f() {
type x; // [1]
return x; // [2]
}
int main() {
type const & r = f(); // [3]
r.const_function();
}
从概念上讲,同样的事情正在发生。在[1]创建了一个对象x
,并将其复制到[2]中的返回值,现在在[3]中返回的值不用于初始化r
,而是初始化一个未命名的由编译器注入的对象。最后,在[3]中,常量引用绑定到未命名对象。此时,未命名的对象位于main
的上下文中。
从当前编译器实际发生的情况来看,调用约定决定了函数接口的实现方式。通过向函数传递一个额外的隐藏指针,实现了一个函数的调用约定,该函数返回一个不适合寄存器的对象(在我所知的所有编译器中)。也就是说,调用者(在这种情况下为main
)保留堆栈中的空间,然后将指向该位置的指针传递给被调用者。这是调用者和被调用者同意返回的对象将存在的位置。这意味着调用者可以在原始示例中为m
保留空间,或在第二种情况下为未命名对象保留空间并将指针传入。这将从等式中删除其中一个对象m
(或未命名的对象)和返回的对象是相同。下一步是由编译器在处理被调用者时执行的,如果它可以确定将在所有代码路径中返回x
,则它不会在自己的堆栈中创建x
,而是直接在内存中创建main
由隐藏指针引用。完成后,最终效果是程序中将有一个对象:
f
将获取内存,并将指针传递给f
,x
将在该内存位置创建main
(位于main
}}'堆栈空间)并返回const
,在{{1}}引用的情况下,编译器通过引用的名称对未命名的对象进行别名。
(注意:引用和未命名的对象不相同,它们的类型实际上可能不同,但引用将是别名到提供的未命名对象它的名字)