lvalue xvalue prvalue

1.所谓泛左值是指一个通过评估能够确定对象、位域或函数的标识的表达式。简单来说,它确定了对象或者函数的标识(具名对象)。

2.而纯右值是指一个通过评估能够用于初始化对象和位域,或者能够计算运算符操作数的值的表达式。

3.将亡值属于泛左值的一种,它表示资源可以被重用的对象和位域,通常这是因为它们接近其生命周期的末尾,另外也可能是经过右值引用的转换产生的。

lvalue xvalue prvalue_第1张图片

从本质上说产生将亡值的途径有两种,

第一种是使用类型转换将泛左值转换为该类型的右值引用,比如:

Widget w1;

通过staic_cast强制转换:Widget&& w = static_cast(w1);

或者通过std::move():Widget&& w = std::move(w1);

第二种在C++17标准中引入,我们称它为临时量实质化,指的是纯右值转换到临时对象的过程。每当纯右值出现在一个需要泛左值的地方时,临时量实质化都会发生,也就是说都会创建一个临时对象并且使用纯右值对其进行初始化,这也符合纯右值的概念,而这里的临时对象就是一个将亡值。

struct X
{
     int value;
};
int main()
{
     int x = X().value;
}

在上面的代码中,X()是一个纯右值,访问其成员变量value却需要一个泛左值,所以这里会发生一次临时量实质化,将X()转换为将亡值,最后再访问其成员变量value。还有一点需要说明,在C++17标准之前

临时变量是纯右值,只有转换为右值引用的类型才是将亡值。

通过代码进一步归纳下值类别:

#include 
#include 
#include 
#include 

template
std::string type_to_string()
{
#if defined(_MSC_VER)
    std::string typeName{__FUNCSIG__};
    auto start = typeName.find_first_of('<', std::string(typeid(std::string).name()).size()) + 1;
    auto end = typeName.find_first_of('>');
    return typeName.substr(start, (end - start));
#elif defined(__clang__)
    std::string typeName{__PRETTY_FUNCTION__};
    auto start = typeName.find_first_of('=') + 2;
    auto end = typeName.find_first_of(']', start);
    return typeName.substr(start, (end - start));
#elif defined(__GNUC__)
    std::string typeName{__PRETTY_FUNCTION__};
    auto start = typeName.find_first_of('=') + 2;
    auto end = typeName.find_first_of(';', start);
    return typeName.substr(start, (end - start));
#endif
}

//lvalue
template
inline constexpr bool is_lvalue = std::is_lvalue_reference_v;
//xvalue
template
inline constexpr bool is_xvalue = std::is_rvalue_reference_v;
//prvalue
template
inline constexpr bool is_prvalue = !(is_lvalue | is_xvalue);

int a = 10;
int temp = 10;

int& f(){return a;}
void g(){}
int&& h(){ return static_cast(temp);}

void(&&g2)() = g;

struct Person
{
int age;
static int widget;
};
//int Person::widget = 0;

int main()
{
    //左值
//1. 普通变量
std::cout << std::boolalpha;
std::cout << "lvalue: " << is_lvalue << std::endl;   // a is lvalue.
    
//2. 函数
std::cout << "lvalue: " << is_lvalue << std::endl; // main() function is lvalue.
    
//3. 成员变量
Person p;
std::cout << "lvalue: " << is_lvalue << std::endl; // p.age is lvalue.
std::cout << "lvalue: " << is_lvalue << std::endl; //Person::widget function is lvalue.

//4. 字符类型的字面量
std::cout << "lvalue: " << is_lvalue << std::endl; // "hello world" is lvalue.
std::cout << type_to_string() << std::endl; // const char (&)[12] is lvalue.

//5. 左值引用类型的函数返回值
std::cout << "lvalue: " << is_lvalue << std::endl; // f type: int(), f()->int& is lvalue.

void(&gr)()=g;
//6. 函数不管怎么变都是左值
std::cout <() << std::endl; // "hello world" is lvalue.
std::cout <() << std::endl; // gr type: void*&)(), &g is lvalue.
std::cout << "lvalue: " << is_lvalue << std::endl; // &gr is lvalue.
    std::cout << type_to_string() << std::endl;//
    std::cout << "lvalue: " << is_lvalue << std::endl;//
    
    //纯右值

//1. 除字符串字面量意外的其他字面量
std::cout << "prvalue: " << is_prvalue << std::endl;  //10 is prvalue.
std::cout << "prvalue: " << is_prvalue << std::endl; //true is prvalue.
std::cout << "prvalue: " << is_prvalue << std::endl; //nullptr is prvalue.

//2. 函数返回值
std::cout << "prvalue: " << is_prvalue << std::endl; //main()->int is prvalue.

//3. 临时对象
std::cout << "prvalue: " << is_prvalue << std::endl;

//4. 取地址
std::cout << "prvalue: " << is_prvalue << std::endl;

//5. lambda表达式[since C++20]
//std::cout << is_prvalue << std::endl;//这种形式的lamba也是个临时对象

    //将亡值
//1. std::move()
int x = 10;
std::cout << "xvalue: " << is_xvalue << std::endl;
std::cout << "xvalue: " << is_xvalue << std::endl;

//2. static_cast
std::cout << "xvalue: " << is_xvalue(10)))> << std::endl;
std::cout << "xvalue: " << is_xvalue(x)))> << std::endl;

//3. 函数范围值和为T&&类型
std::cout << "xvalue: " << is_xvalue << std::endl;

//4. rvalue对象的非静态成员
Person p2;
std::cout << "xvalue: " << is_xvalue << std::endl;
std::cout << "xvalue: " << is_xvalue << std::endl;

}

你可能感兴趣的:(C++17,C++,lvalue,xvalue,prvalue)