C++型別推導的“坑”——忽略引用


無論是模板還是auto的型別推導,絕大部分情況下都會忽略引用。舉個栗子:

template<typename T>
void f(T param) {

如果現在有int類型的變量x和它的引用rx,當rx傳給函數f時,我們的直覺是T應該被推導為引用,由此直覺還認為定義一個T類型的變量temp並用param賦值后,temp和param應該都是x的引用。然而直覺是錯誤的,這里T會被推導為int,而非int&。可以用代碼測試一下,如果temp也是引用,那么對temp另外賦值應該會更改x的值:

template<typename T>
void f(T param) {
    T temp = param;
    temp = 5; // 若temp是引用,那么這條語句會把x的值改為5
}

//main函數中:
int x = 2;
int& rx = x;
f(rx); //實參為int&
cout << x; // 看看x的值是否被改變

最終輸出結果為2,說明T只是int類型,temp是一個拷貝而非引用。

auto的型別推導同樣。

只有一個情況例外:當形參是萬能指針而實參是左值時,T為左值引用。也就是說將上面代碼中f的形參類型改為T&&,此時再運行代碼會發現輸出為5,說明T為int&。

我們有時會忘記型別推導忽略引用這個特點。比如,大多數容器的operator []會返回T&,因此當我們希望寫一個函數,該函數可以返回位於某容器特定下標位置的元素時,我們可能會寫出這樣的代碼:

/*向該函數傳入一個容器和一個下標;函數返回位於這個容器該下標位置的元素的引用*/
template<typename Container, typename Index>
auto find_ele(Container& c, Index i) {
    do_sth();
    return c[i];
}

然后會像下面這樣來使用這個函數:

find_ele(arr, 3) = 5;

然后會發現報錯了。編譯器會說find_ele(arr, 3)並非左值,不能被賦值。這就是因為雖然return c[i]返回的是引用,但auto型別推導時卻會忽略引用。
如果希望將引用作為返回,可以像下面這樣寫函數頭部(僅考慮實參為左值的情況):

//(1)
auto& find_ele(Container& c, Index i)
//(2)
auto find_ele(Container& c, Index i) -> decltype(c[i])
//(3)
decltype(auto) find_ele(Container& c, Index i)

(1)顯式規定返回值必須是引用;(2)和(3)都使用了decltype,這是因為decltype在一般情況下都會忠實地返回參數自身的類型而不做更改。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM