右值引用的生命周期
Lifetime of rvalue ref
下面的代码工作正常,据我所知,每次调用该函数时,都会创建一个局部变量(即向量),并且所有权将在第一次调用时以右值引用和 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 特性,而第二个是传统特性。
我刚刚用自定义 class 做了一个例子, 复制构造函数不会被调用 ,但是它将像上面的示例一样工作!
您的 get_v
函数 return 是一个值,因此 v
超出范围并不重要,因为您 return 它的 值.
与以下内容没有区别:
int a()
{
int j = 3;
return j; // returns the *value* of j
}
C++ 标准允许编译器通过某种方式将 v
转发给调用者来省略复制构造函数。如果绑定了引用,C++ 标准还要求延长临时对象的生命周期。
当您编写 std::vector<int> &&rval_ref = get_v();
时,这些是达到 return v;
时的概念性步骤
- 创建了一个名为 return 值 的临时对象。该对象如同
std::vector<int> x{v};
一样被初始化。它在 main
中 "lives" 并且在 main
中的这个语句完成时自然会 "go out of scope"。
v
被摧毁
- 引用
rval_ref
绑定到该临时对象。将引用绑定到临时对象会导致对象的生命周期延长以匹配引用的生命周期。
这个名字 "temporary object" 有点用词不当,因为该物体实际上可能会持续很长时间,但这仍然是官方名称。 "Unnamed object" 是另一种可能的描述。
您的引用不引用 v
,它们引用它的副本(因为您的函数 returns 按值)。所以即使 v
被销毁,副本也不会被销毁。
由于复制省略,您的测试代码未显示复制构造函数调用(上述第 1 步)。复制省略意味着编译器可以选择为 v
使用与 return 值 相同的内存 space;并省略 v
的析构函数和 return 值的复制构造函数(即使这些函数有副作用)。
下面的代码工作正常,据我所知,每次调用该函数时,都会创建一个局部变量(即向量),并且所有权将在第一次调用时以右值引用和 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 特性,而第二个是传统特性。
我刚刚用自定义 class 做了一个例子, 复制构造函数不会被调用 ,但是它将像上面的示例一样工作!
您的 get_v
函数 return 是一个值,因此 v
超出范围并不重要,因为您 return 它的 值.
与以下内容没有区别:
int a()
{
int j = 3;
return j; // returns the *value* of j
}
C++ 标准允许编译器通过某种方式将 v
转发给调用者来省略复制构造函数。如果绑定了引用,C++ 标准还要求延长临时对象的生命周期。
当您编写 std::vector<int> &&rval_ref = get_v();
时,这些是达到 return v;
时的概念性步骤
- 创建了一个名为 return 值 的临时对象。该对象如同
std::vector<int> x{v};
一样被初始化。它在main
中 "lives" 并且在main
中的这个语句完成时自然会 "go out of scope"。 v
被摧毁- 引用
rval_ref
绑定到该临时对象。将引用绑定到临时对象会导致对象的生命周期延长以匹配引用的生命周期。
这个名字 "temporary object" 有点用词不当,因为该物体实际上可能会持续很长时间,但这仍然是官方名称。 "Unnamed object" 是另一种可能的描述。
您的引用不引用 v
,它们引用它的副本(因为您的函数 returns 按值)。所以即使 v
被销毁,副本也不会被销毁。
由于复制省略,您的测试代码未显示复制构造函数调用(上述第 1 步)。复制省略意味着编译器可以选择为 v
使用与 return 值 相同的内存 space;并省略 v
的析构函数和 return 值的复制构造函数(即使这些函数有副作用)。