右值ref的生命周期

Lifetime of rvalue ref

本文关键字:周期 生命 ref 右值      更新时间:2023-10-16

下面的代码工作得很好,据我所知,每次调用函数时,都会创建一个局部变量(即向量),并且在第一次调用时将所有权转移到右值引用中,并在第二次调用时转移到const引用中(如果我删除它甚至不会编译)。因此,当函数终止时,局部变量实际上并没有死亡,但是当main中的引用超出作用域时(即在main()的末尾),我认为!

#include <iostream>
#include <vector>
std::vector<int> get_v(void)
{
    std::vector<int> v{1,2,3};
    return v;
}
int main() {
    std::vector<int> &&rval_ref = get_v();
    for(unsigned int i = 0; i < rval_ref.size(); ++i)
        std::cout << rval_ref[i] << "n";
    const std::vector<int> &con_ref = get_v();
    for(unsigned int i = 0; i < con_ref.size(); ++i)
        std::cout << con_ref[i] << "n";
    return 0;
}
输出:

gsamaras@pythagoras:~$ g++ -std=c++0x -Wall px.cpp
gsamaras@pythagoras:~$ ./a.out
1
2
3
1
2
3

但是我认为局部变量在超出作用域时就会死亡,除非在它们之前有static关键字,或者它们已经被动态分配,甚至被复制。在这种情况下,不复制向量。也许我的C背景让我无法理解这里的概念。你能帮我一下吗?

作为旁注,第一种情况允许修改向量,而第二种情况显然不能。我猜第一个是c++ 11的特性,而第二个是传统的。


我刚刚用一个自定义类做了一个例子,复制构造函数不会被调用,但它会像上面的例子一样工作!

当你写std::vector<int> &&rval_ref = get_v();时,这些是达到return v;时的概念步骤

  1. 创建一个临时对象,称为返回值。该对象与std::vector<int> x{v};一样初始化。它"存在"在main中,并且当main中的此语句完成时自然会"超出范围"。
  2. v被销毁
  3. 引用rval_ref被绑定到这个临时对象。将引用绑定到临时对象会导致对象的生存期延长,以匹配引用的生存期。

"临时对象"这个名字有点用词不当,因为对象实际上可能会持续很长时间,但这是正式名称。"未命名对象"是另一种可能的描述。

你的引用不指向v,它们指向它的副本(因为你的函数按值返回)。因此,即使v被销毁,副本也不会被销毁。

您的测试代码没有显示复制构造函数调用(上面的第1步),因为复制省略。复制省略意味着编译器可以选择为v使用与返回值相同的内存空间;并省略v的析构函数和返回值的复制构造函数(即使这些函数有副作用)。

您的get_v函数返回一个值,因此v超出作用域并不重要,因为您返回其

没有区别
int a()
{
    int j = 3;
    return j; // returns the *value* of j
}

c++标准允许编译器通过某种方式将v转发给调用者来省略复制构造函数。c++标准还要求,如果将引用绑定到临时对象上,则需要延长其生命周期。