這篇先簡單討論C++中的幾個basic concepts: lvalue, rvalue,lvalue reference, rvalue reference,cv-qualified, reference collapsing rule等等。這是理解template argument deduction的基礎。(Mirror)shell
陳浩(coolshell)說技術文章要通暢,在地鐵或馬桶上能夠一口氣看完的文章纔是好文章。 要寫的深刻淺出是要花些時間的。反正這篇是作不到了, 稍稍有點兒枯燥。😃express
C++98,03裏有lvalue和rvlaue的概念。app
簡單的說,lvalue一個在內存有肯定地址的對象,能夠尋址。而rvalue是一個臨時對象。less
An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address).ide
--cited from herepost
看例子:ui
int a = 1; //a is an lvalue, 1 is an constant // 1 = a; //Error! 1 is rvalue. //(var + 1) = 0; //Error. expression (var + 1) is an rvalue.
Ok, 到此,很熟悉很簡單。 可是,不復雜不舒服斯基的C++在C++11裏進行了更細緻的分類,增長了三個categoriesthis
爲何這麼搞?
由於C++11引進了rvalue reference,在overload resolution,template argument deduction等地方,須要對expression的進行更細的分類。
它們之間的關係,看Venn圖。from 這裏.net
----- Expression category taxonomy ----- ______ ______ / X \ / / \ \ | l | x | pr | \ \ / / \______X______/ gl r // l is lvalue, pr is prvalue, x is xvalue, gl is glvalue, r is rvalue.
上圖lvalue和rvalueC++11大體和C++98是同樣的,只不過把rvalue分爲了xvalue和pvalue. 而glvalue分爲xvalue和lvalue
再看標準,code
Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue,
xvalue, or prvalue. This property of an expression is called its value category. (N3690)
一個expression的value category有三種: lvalue, prvalue, xvalue,這三種爲基本的value category。
接下來看嚴謹一點兒的定義,但不徹底,先忽略不經常使用的狀況。
an lvalue is an expression that identifies a non-temporary object or a non-member function.
(N3690 3.10)
An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
簡單起見,只列出常見的狀況:
int a; //i is an lvalue int&& arref = a; //arref is an lvalue, but it's type is 'rvalue reference' ++a; //++a is an lvalue int arr[] = {1,2}; int* p = &arr[0]; //arr, arr[0] and p are both lvalue *(p+1) = 3; //dereference. expression (p+1) is an rvalue, but *(p+1) is an lvalue "hello"; //"hello" is an lvalue
A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
下列expressions是prvalues:
23L // 23L is an pvalue int a; a++; //a++ is an pvalue (1+2); // (1+2) is an pvalue (int) 1.2; // (int)1.2
An xvalue (an 「eXpiring」 value) also refers to an object, usually near the end of its lifetime (so that its
resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving
rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue
reference is an xvalue. —end example ]
An xvalue is an expression that identifies an "eXpiring" object, that is, the object that may be moved from. The object identified by an xvalue expression may be a nameless temporary, it may be a named object in scope, or any other kind of object, but if used as a function argument, xvalue will always bind to the rvalue reference overload if available.
下列expressions是xvalues:
Like prvalues, xvalues bind to rvalue references, but unlike prvalues, an xvalue may be polymorphic. and a non-class xvalue may be cv-qualified. (since C++11)
glvalue和rvalue就不談,可看ISO的標準文檔。到如今,看下面例子,應該很好理解了。
int prvalue(); int& lvalue(); int&& xvalue();
有一點須要注意,「class rvalues can have cv-qualified types, but built-in types (like int) can't 」 在研究tempalte argument deduction的時候,這一點曾讓我迷惑過,雖然如今看起來比較天然,看代碼:
struct Foo {}; Foo f() { return Foo(); } const Foo cf() { return Foo(); } int b() {int i=0; return i;}; //return built-in type rvalue const int cb() {int i=0; return i;} // try to add const template<typename T> void g(T&& t) {} int main () { g (f()); //calls: g<Foo>(Foo&&) g (cf()); //calls: g<Foo const>(Foo const&&). g (b()); //calls: g<int>(int&&) g (cb()); //calls: g<int>(int&&) Notice:no const version! return 0; }
若不理解T是咋deduce出來的,可暫且無論. 經過對比,至少能夠得出built-in type rvalue沒有cv-qualifiyed版本。
C++的Types分爲三種:Fundamental types, Compound Types, CV-qualifiers
Fundamental types:是指bool, int, char 等等。
Compound Types:包括:
CV-qualifiers: 指 const qualifier和volatile qualifier
有了上面Types的分類,再來研究下lvalue reference和rvalue reference。
C++ ISO文檔裏談到的術語reference,包含了兩種,lvalue reference和rvalue reference.
lvalue reference語法形式是 T&, rvalue reference是 T&&。 rvalue reference和lvalue reference差很少,特殊地方在於rvalue reference能綁定到一個臨時變量上,而非const的rvalue reference不能。
A a; A& a_ref = a; // an lvalue reference A a; A&& a_rref = a; // a_rref's type of rvalue reference // but a_rref is an lvalue, because the named rvalue references are lvalues. void foo(int&& t) { // t is initialized with an rvalue expression // but is actually an lvalue expression itself, here t is a named rvalue reference. } A& a_ref3 = A(); // Error! A&& a_ref4 = A(); // Ok
a_rref's type of rvalue reference but a_rref is an lvalue, because the named rvalue references are lvalues. 稍有晦澀。術語lvalue / rvalue 是表達式的分類(value categories of Expressions), 不是C++的Types(類型);而lvalue reference/rvalue reference是compound type是一種type.
If a typedef (7.1.3), a type template-parameter (14.3.1), or a decltype-specifier (7.1.6.2) denotes a type TR
that is a reference to a type T, an attempt to create the type 「lvalue reference to cv TR」 creates the type
「lvalue reference to T」, while an attempt to create the type 「rvalue reference to cv TR」 creates the type TR.
-- cited from ISO N3690
typedef int& lref; typedef int&& rref; int n; lref& r1 = n; // type of r1 is int& lref&& r2 = n; // type of r2 is int& rref& r3 = n; // type of r3 is int& rref&& r4 = 1; // type of r4 is int&&
這個規則和template argument deduction 中的T&& special case, 就是std::forward實現的基礎。
Universal References in C++11 -- by Scott Meyers