矢量仍然存在吗?

时间:2011-11-13 23:17:34

标签: c++ visual-c++

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()堆栈空间,因为结果向量在两个方法中都有相同的地址。

4 个答案:

答案 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将获取内存,并将指针传递给fx将在该内存位置创建main(位于main }}'堆栈空间)并返回const,在{{1}}引用的情况下,编译器通过引用的名称​​对未命名的对象进行别名。

(注意:引用和未命名的对象相同,它们的类型实际上可能不同,但引用将是别名到提供的未命名对象它的名字)