C++中表达式的值类别
C++中的表达式有两个属性,分别是值类型和值类别,本篇文章着重于表达式的值类别。C++中表达式的值类别分为以下几种
- 泛左值(glvalue,generalized lvalue):这种表达式决定一个对象或者函数的标识,比如变量名,函数名。
- 纯右值(prvalue,pure rvalue):
- 计算某个运算对象的内置运算符的值,这种prvalue没有结果对象。
- 初始化一个对象,这个结果变量可能是一个变量,一个new表达式创建的对象,由temporary materialization创建的临时对象等等。
- 将亡值(xvalue,expiring value):将亡值是对象的资源被标记为可以被重复使用的泛左值。
- 左值(lvalue):左值指的是不是将亡值的泛左值。
- 右值(rvalue):纯右值和将亡值统称为右值。
仅仅从定义来看会很难理解这些概念,下面例举一些例子,可以帮助更好的理解。
左值
- 变量名,函数名或者数据成员,无视类型。无论变量是左值引用或者右值引用,由变量名称组成的表达式始终是一个左值表达式
- 一个函数调用或者运算符重载表达式,它们的返回类型为左值引用,例如
std::cout << 1
- 内置的赋值和复合赋值运算符,例如
a = 1
,b += 1
- 内置的前置递增或者递减运算符,
++a
和--a
- 字符串字面量
- 一个类型转换表达式,目标类型为左值引用
- 一个函数调用或者运算符重载表达式,它的返回类型为函数的右值引用
- 一个类型转换表达式,它的目标类型为函数的右值引用
纯右值
- 字面量,例如
42
,true
,nullptr
。注意字符串字面量不是纯右值。 - 一个函数调用或者运算符重载表达式,它们的返回类型为非引用类型,例如
str.substring(1,2)
。 a++
和a--
,内置的后缀递增或者递减运算符a+b
,a-b
,a & b
,a << b
,其他内置的算术运算符a && b
,a || b
,!a
,内置的逻辑运算符a < b
,a == b
,a > b
,内置的比较运算符&a
,内置的取址运算符- 一个类型转换表达式,目标类型为非引用类型,例如
static_cast<double>(x)
- this指针
- 标量类型的非类型模板参数
- 一个lamada expression
将亡值
- 一个函数调用或者函数重载表达式,它的返回类型是一个右值引用,例如
std::move(x)
- 一个类型转换表达式,它的目标类型为右值引用,例如
static_cast<char&&>(r)
- 任何指明一个临时对象的表达式,在temporary materialization
这里需要注意的是temporary materialization,任何完整类型的纯右值都可以被转换为一个相同类型的将亡值,这种转换初始化一个临时对象,通过纯右值来计算一个临时对象来作为结果对象。例如
struct S {int x};
int i = S().x;//S()纯右值被转换为将亡值
以下几种情况会造成将亡值的出现
- 绑定一个引用到纯右值
- 对一个类纯右值实施成员访问
- 当从花括号初始化列表初始化一个
std::initializer_list<T>
对象。
将亡值表达式也会标识一个对象,毕竟它也属于泛左值这个大类里面,与左值不同的是,将亡值会将对象标记为资源可复用的对象,这在移动语义里面起着至关重要的作用。
模板函数类型推导
在理解了值类别之后,就可以开始讨论模板函数的类型推导了。整个模板的类型推导十分复杂,这里不会覆盖整个模板推导,仅仅包含模板函数的推导,而且只与引用有关。
按引用传递
考虑如下模板函数
template<typename T>
void foo(T& t)
{
}
这个模板函数的参数是按引用传递,所以可以传递左值表达式,需要注意的是,即使你传递的是右值引用,它仍然还是一个左值表达式,如下代码所示
int&& a = 1;
foo(a);
但是这种形式下,传右值是不可以的,因为如果要左值引用右值,它必须是一个常引用,因此,如下代码是正确的
在文章开始之前,我想先定义一个概念,对象。在不同的编程语言中,对象有着不同的解释,但是在本篇文章中,我想将对象定义为存在于内存中的,由某种类型描述的数据。例如,函数中的int变量,它位于栈中,int类型表明了对象的大小。
class Foo
{
};
foo(Foo());//纯右值
Foo f;
foo(std::move(f));//将亡值
引用转发
在讲右值引用之前,我想先讲引用转发,引用转发是形如以下的模板函数神声明
template<typename T>
void foo(T&& t)
{
}
我们用T&&
形式来标识一个右值引用。右值引用的作用是,当你传递一个左值表达式时,模板类型参数会被推导为一个左值引用,例如如下代码
int a = 1;
foo(a);//因为a是一个左值表达式,所以T被推导为int&
如果传递的是一个右值,那么模板类型参数会被推导为右值表达式的值类型,例如如下代码
foo(1);//纯右值,T被推导为int
foo(std::move(1));//将亡值,T被推导为int
右值引用
右值引用非常类似于引用转发,它的形式如下
template<typename T>
void foo(const T&& t)
{
}
右值引用比引用转发多了一个const
,而且只能传递右值表达式,因此以下的代码是不正确的
int a =1;
foo(a);//我们传递了一个左值表达式,但是foo模板函数只能接收右值表达式
标签:推导,右值,左值,C++,运算符,模板,表达式,引用
From: https://www.cnblogs.com/riasartemis/p/17099475.html