核心代码取自http://www.talkplayfun.com/
概念来源于: 1.维基百科 2.上述网站的建立者 3.C++.Templates.The.Complete.Guide.2nd(第一版是零几年的,第二版好像没有中文版,嗯,好像。。。)
光通过“看”去区分lvalue和rvalue可能难以真正明白这些概念的,所以这个时候就需要从代码的角度看看这些概念到底是什么玩意。
一些说明:
1. 翻译不一定准确 sorry for my broken English...
2. decltype(a) 返回的是a的类型(type category), decltype((a)) 返回的是a的值类型(value category) 二者并不等价
3. 在c++中,值本身是有类型的,比如 int a = 0; a的类型是int, a的值类型是int&,二者类型并不一致
4. 在c++中,lvalue和rvalue是value category, lvalue reference和rvalue reference是type category 二者并不是一个东西
5. 值可以细分为三个类型:lvalue(左值), xvalue(亡值? 貌似是说生命周期非常短), prvalue(纯右值, 比如一些字面量), 其中lvalue和xvalue叫做泛左值(glvalue), prvalue和xvalue叫做右值(rvalue)
6. 左值是可以取地址的,亡值通常生命周期极短, 纯右值包括一些字面量如0,1,2,3...,但不包括字符串,比如“hello world”
剩下的看看英文吧
/** * expression category * / * glvaue rvalue * / / * lvalue xvalue prvalue */ // lvalue: an expression that has an identity and which we can take address of // xvalue: "eXpiring lvalue" - an object that we can move from, which we can // reuse. Usually, its lefetime ends soon // prvalue: pure rvalue - something without a name, which we cannot take the // address of, we can move from such expression (just as initialization) -> move // and copy will be elided // glvalue: "generalised lvalue" - A glvalue is an expression whose evaluation // computers the location of an object, bit field, or function /** * class X { int a; }; * X{10}; -> prvalue * X x; -> lvalue * x.a; -> lvalue * X xx = CreateX(); -> prvalue */ #include <iostream> #include <string> #include <type_traits> template <typename T> constexpr bool is_lvalue_helper = std::is_lvalue_reference_v<T>; template <typename T> constexpr bool is_xvalue_helper = std::is_rvalue_reference_v<T>; template <typename T> constexpr bool is_prvalue_helper = !(is_lvalue_helper<T> || is_xvalue_helper<T>); template <typename T> constexpr bool is_rvalue_helper = is_xvalue_helper<T> || is_prvalue_helper<T>; template <typename T> constexpr bool is_glvalue_helper = is_xvalue_helper<T> || is_lvalue_helper<T>; #define Is_Lvalue(type_instance) is_lvalue_helper<decltype((type_instance))> #define Is_Xvalue(type_instance) is_xvalue_helper<decltype((type_instance))> #define Is_PRvalue(type_instance) is_prvalue_helper<decltype((type_instance))> #define Is_Rvalue(type_instance) is_rvalue_helper<decltype((type_instance))> template <typename T> std::string type_to_str() { /* #if defined(_MSC_VER) std::string type_name(__FUNCSIG__); #elif defined(__GNUC__) std::string type_name(__PRETTY_FUNCTION__); #elif defined(__clang__) std::string type_name(__PRETTY_FUNCTION__); #endif */ // __GNUC__ // std::string type_to_str() [with T = int; std::string = // std::basic_string<char>] std::string s{__PRETTY_FUNCTION__}; auto start = 36; auto end = s.find_first_of(';', start); return s.substr(start, end - start); } #define GetTypeCategory(type_instance) type_to_str<decltype(type_instance)>() #define GetValueCategory(type_instance) type_to_str<decltype((type_instance))>() #define STRING(STR) #STR #define Print_Value_Info(type_instance) std::cout << STRING(type_instance) << " is prvalue ? " << Is_PRvalue(type_instance) << std::endl; std::cout << STRING(type_instance) << " is xvalue ? " << Is_Xvalue(type_instance) << std::endl; std::cout << STRING(type_instance) << " is lvalue ? " << Is_Lvalue(type_instance) << std::endl; std::cout << STRING(type_instance) << " is rvalue ? " << Is_Rvalue(type_instance) << std::endl auto f = [](int x) { return x; }; int c = 0; void test1() { std::cout.setf(std::ios::boolalpha); std::cout << GetTypeCategory(c) << std::endl; std::cout << GetValueCategory(c) << std::endl; std::cout << GetValueCategory(f) << std::endl; std::cout << GetTypeCategory(f) << std::endl; } void test2() { int a = 0; int& r_a = a; int&& rr_a = 0; Print_Value_Info(a); Print_Value_Info(r_a); Print_Value_Info(rr_a); } struct X { int a; }; X CreateX(){
return {};
}; void test3() { /** * class X { int a; }; * X{10}; -> prvalue * X x; -> lvalue * x.a; -> lvalue * X xx = CreateX(); -> prvalue */ std::cout.setf(std::ios::boolalpha); X x; std::cout << Is_PRvalue(X{10}) << std::endl; std::cout << Is_Lvalue(x) << std::endl; std::cout << Is_Lvalue(x.a) << std::endl; std::cout << Is_PRvalue(CreateX()) << std::endl; } int main(int argc, char const* argv[]) { std::cout << "=============================" << ' '; // test1(); // test2(); test3(); return 0; } /* int a = 2; // a's declared type is int and its value category is lvalue // 2 has no declared type, but its deduced type is int and its value // category is prvalue int& r = a; // r's declared type is lvalue reference and value category is lvalue int&& rr = 5; // rr's declared type is rvalue reference and value category is lvalue // 5 -> int, its value category is prvalue // decltype() return type category // decltype( () ) return value category An expression is C++ has two category type category and value category lvalue reference, rvalue reference value category - if we can assign or access or modify the expression lvalue, prvalue, xvalue, glvalue, rvalue usually in CPU register */