2014-01-12 53 views
4

我使用auto的一些情況:汽車演繹類型如何?

auto s = expr;   //s is always lvalue 
auto & s = expr;  //s is always lvalue reference? What if expr is rvalue? 
auto && s = expr;  //s is perfectly forwarded 

他們是真的嗎?如果不是,爲什麼?

+0

在一個變量聲明爲推導'auto'類型是通過模板參數推導規則定義,見[dcl.spec.auto]/6;但有一個例外:如果初始化器是一個braced-init-list,則推導的類型是一個'std :: initializer_list'。 – dyp

+0

順便說一句,作爲表達式的's'總是*左值,因爲's'是一個標識符(並且不是枚舉符)。左值/右值是表達式的類別。 – dyp

+0

P.S.這也是*斯科特邁耶的通用參考文章(http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers);) – dyp

回答

5

dyp是正確的,我想詳細說明。

首先,得出的結論是從DYP:

在一個變量聲明爲推導出的auto類型經由模板參數推導規則定義 ,見[dcl.spec.auto ]/6; 但有一個例外:如果初始化程序是一個加載初始化列表,則 推導的類型是std::initializer_list

我會解釋一下。

首先,

auto s = expr; 

這是一樣的推斷來自exprT

template<class T> 
void f(T s); 

f(expr); 

模板參數推導規則是相當複雜的,因爲你只用左值和右值有關東西,讓我們專注於此。

模板參數推導是通過(在這種情況下是PT稱之爲P,)模板參數類型進行比較,以及相應的參數(稱之爲A,在這種情況下,expr類型)。

從14.8.2.1,

如果P不是引用類型:

- 如果A是一個數組類型,由陣列到指針標準轉換所產生的指針類型(4.2 )是 用於代替A的類型扣除;否則,

- 如果A是一個函數類型,則由函數指針標準轉換生成的指針類型(4。3)用 代替A進行類型扣除;否則,

- 如果A是cv限定類型,則A類型的頂級cv限定符在類型推導中將被忽略。

所以,如果expr是陣列或功能,這將被視爲指針,如果expr具有cv-限定符(const等),它們將被忽略。

如果P是CV-限定的類型,P的類型的頂層cv修飾符被忽略類型推演。

這實際上說:

const auto s = expr; 

sconst變量,但類型扣除auto目的,const將被刪除。

因此,從上述規則,auto將被推斷爲類型expr(經過上述一些類型轉換後)。

請注意,當表達式是對T的引用時,在事先分析之前它將被調整爲T

因此無論expr是 - 右值,左值還是右值/右值ref類型 - auto的類型將始終爲expr的類型,而不帶參考。

auto s1 = 1; //int 
int &x = s1; 
auto s2 = x; //int 
int &&y = 2; 
auto s3 = y; //int 

其次,讓我們來看看

auto &s = expr; 

這將是相同

template<class T> 
void f(T &s); 

f(expr); 

從標準的額外的規則如下:

如果P是參考類型,由引用用於類型扣除。

所以汽車的扣將作爲無&完全相同,但扣除auto類型後,&被添加到auto末。

//auto &s1 = 1; //auto is deducted to int, int &s1 = 1, error! 
const auto &s1 = 1; //auto is deducted to int, const int &s1 = 1; ok! 
const int &x = s1; 
auto &s2 = x; //auto is int, int &s2 = x; ok! 
int &&y = 2; 
auto &s3 = y; //auto is int, int &s3 = y; ok! 

請注意,最後的y是一個左值。 C++的規則是:名稱右值引用是一個左值。

最後:

auto &&s = expr; 

這是毫無疑問的一樣

template<class T> 
void f(T &&s); 

f(expr); 

一個從標準的附加規則:

如果P是一個右值引用的cv-不合格模板參數和 參數是一個左值,類型「左值參考A」是用於 的地方A進行類型扣除。

這實際上說,如果expr是一個rvalue,該規則將是相同於該第二殼體(左值的情況下),但如果expr是一個左值,的A類型將是一個左值參考A

從前面解釋的注意,A永遠不會引用,因爲表達式的類型永遠不會是引用。但對於這種特殊情況(auto &&A是左值),必須使用對A的引用,而不管expr本身是否爲引用類型。

實施例:

auto &&s1 = 1; //auto is deducted to int, int &&s1 = 1, ok! 
int x = 1; 
auto &&s2 = x; //x is lvalue, so A is int &, auto is deducted to int &, int & &&s2 = x; ok! 
int &&y = 2; 
auto &&s3 = y; //y is lvalue, auto is int &, int & &&s3 = y; ok! 
+1

最後一點我被混淆了:'auto&= makeSomething();',用'sometype makeSomething();':如果'sometype'是非const non-ref,這會失敗,因爲'auto'被推斷到'sometype'和'sometype&= makeSomething();'試圖將一個prvalue綁定到一個左值ref。如果'sometype' const(無參考),但是非類非數組,常量將根據[expr]/6被丟棄,並且發生相同的情況。如果'sometype'是一個常量類類型('使用sometype = myclass const;'),那麼這會工作,因爲'auto'被推斷爲'sometype const'(和'sometype const&s = makeSomething();'是合法的) 。 – dyp

+0

使用和typedef將不會定義新類型,您的示例將不合法,因爲auto會被推斷爲myclass,而不是某種類型。 – user534498

+0

糟糕,這是一個錯字:)它應該是:如果'sometype'是一個const類型,例如''使用sometype = myclass const;',那麼這將工作,因爲'auto'被推斷爲'myclass const'(和'myclass const&s = makeSomething();'是合法的)。 (順便說一句:'sometype const'只是多餘的,等同於'sometype') – dyp

0

由於沒有人給出答案,我現在有點了解(感謝@dyp),我只是在回答自己的問題。指出錯誤,如果任何:

auto s = expr; 

如果expr是左值,prvalue,或任何參考,s始終是一個左值和一個副本。

auto& s = expr; 

如果expr是左值,左值參考或右值引用,s是左值參考。 如果expr是預估價值,這是一個錯誤,並且不合法。

auto&& s = expr; 

s完美轉發(右值將成爲右值引用和摺疊)。