C++的值类别
2021-06-21
4 min read
C++的值类别
值类别
C++表达式有两种属性:类型(type)和值类别(value category)。其中类型主要区别值的大小与解读方式,此属性是大家熟知的,故不赘述,以下内容主要阐述值类别。
每个表达式只属于三种值类别中的一种,纯右值(prvalue)、亡值(xvalue)、左值(lvalue);纯右值和亡值统称为右值(rvalue),亡值和左值统称为泛左值(glvalue);以上对于值类别的表述可能难以理解,以下进行详细解释;
在C++11中,值类别主要用以表达区别表达式是否拥有身份以及是否可被移动:
- 拥有身份(泛左值):可以确定表达式是否与另一表达式指代同一实体,例如通过比较它们所标识的对象或函数的地址;
- 可被移动(右值):移动构造函数、移动赋值运算符或实现了移动语义的其他函数重载能够绑定于这个表达式;
即拥有身份的表达式被称作泛左值表达式,可被移动的表达式被称作右值表达式,同时又有:
- 拥有身份 且 不可被移动 的表达式被称作*左值 (lvalue)*表达式 [只属于泛左值表达式];
- 拥有身份 且 可被移动 的表达式被称作*亡值 (xvalue)*表达式 [既属于泛左值表达式,又属于右值表达式];
- 不拥有身份 且 可被移动 的表达式被称作*纯右值 (prvalue)*表达式 [只属于右值表达式];
- 不拥有身份 且 不可被移动 的表达式无法使用;
使用树状图直观展示如下:
[需复制下述文字并使用typora打开]
graph TB
A(表达式)
B(泛左值)
C(右值)
D(左值)
E(亡值)
F(纯右值)
A-->B
A-->C
B-->D
B-->E
C-->E
C-->F

移动语义
在C++11中引入新的值类别的概念,其中重要目的就是应用移动语义,以此减少很多不必要的拷贝,从而大幅度提升性能;
实现“移动”主要是依靠std::move(), 其主要功能其实并不是真正的移动,而是将表达式值类别强制转换为右值型别,其具体实现在C++STL中如下所示 [ 代码片段来自 MinGW7.3.0 中的 move.h ]
/**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{
return static_cast<typename std::remove_reference<_Tp>::type&&>(__t);
}
应用举例
下述例子并不能体现 std::move() 的优点,只是作为一个简单例子进行说明:
#include <iostream>
#include <cassert>
#include <memory>
using std::cout;
using std::endl;
using std::unique_ptr;
class Demo {
public:
Demo() = default;
Demo(const Demo& rhs) = default;
Demo(Demo&& rhs) = default;
Demo& operator=(const Demo& rhs) = default;
Demo& operator=(Demo&& rhs) = default;
~Demo() = default;
private:
int m_a = 1;
// ...
};
int main()
{
unique_ptr<Demo> ptrDemo = std::make_unique<Demo>();
cout << "ptrDemo : " << ptrDemo.get() << endl;
// unique_ptr不支持拷贝构造与拷贝赋值操作
unique_ptr<Demo> ptrDemo2 = std::move(ptrDemo);
assert(ptrDemo == nullptr);
cout << "ptrDemo : " << ptrDemo.get() << endl;
cout << "ptrDemo2 : " << ptrDemo2.get() << endl;
// ...
return 0;
}
终端输出:
// console output :
ptrDemo : 0x1304b68
ptrDemo : 0
ptrDemo2 : 0x1304b68
参考
C++11的value category(值类别)以及move semantics(移动语义)
版权声明:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!