啓用C++11編譯老代碼的時候,g++報錯。示例代碼以下:html
#include <utility> int main() { int a = 0; auto b = std::make_pair<int, int>(a, 1); return 0; }
出錯信息:c++
test.cpp: In function ‘int main()’: test.cpp:5:41: error: no matching function for call to ‘make_pair(int&, int)’ auto b = std::make_pair<int, int>(a, 1); ^ test.cpp:5:41: note: candidate is: In file included from /usr/include/c++/4.8/utility:70:0, from test.cpp:1: /usr/include/c++/4.8/bits/stl_pair.h:276:5: note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) make_pair(_T1&& __x, _T2&& __y) ^ /usr/include/c++/4.8/bits/stl_pair.h:276:5: note: template argument deduction/substitution failed: test.cpp:5:41: note: cannot convert ‘a’ (type ‘int’) to type ‘int&&’ auto b = std::make_pair<int, int>(a, 1); ^
從出錯信息裏已經能夠看到C++11版本std::make_pair的原型爲:函數
template<class _T1, class _T2> std::make_pair(_T1&&, _T2&&);
因爲代碼裏直接指定了類型,因而std::make_pair實例化爲:spa
std::make_pair(int&&, int&&);
即std::make_pair接受兩個int類型的右值,因爲變量a不是右值,因而報錯。就好比下面這條語句不能編譯經過同樣:.net
int &&b = a; // error: cannot bind ‘int’ lvalue to ‘int&&’
事實上,示例代碼裏並非std::make_pair的正確用法,正確的用法是無需指定類型,讓編譯器自動推導,即:
code
auto b = std::make_pair(a, 1);
那爲何這樣就能編譯經過呢?
htm
若模板函數的參數類型爲右值引用,如:
blog
template<typename T> void foo(T&&);
那麼T的推導規則爲:當 foo的實參是A類型的左值時, T的類型是 A&;當 foo的是實參是 A類型的右值時, T的類型是 A。ip
根據上一條規則,當 foo的實參爲 A類型的左值時, foo將變成:
get
void foo(A& &&);
出現了引用的引用。在C++裏,引用的引用是不存在的。爲了解決這個問題,C++11引入了引用摺疊規則,對於因爲類型推導而出現的引用的引用,根據如下規則摺疊:
A& & => A& A& && => A& A&& & => A& A&& && => A&&
因此當 foo的實參爲 A類型的左值時, foo事實上會變成:
void foo(A&);
而對於 std::make_pair的例子,當不顯式指定類型時,根據以上兩個規則, std::make_pair將被實例化成:
std::make_pair(int&, int);
和實參類型匹配,因而編譯經過。
右值引用看似簡單,實際上是有不少點須要特別注意的。Scott Meyers都認爲這是C++11裏面最重要的特性,而且容易搞混。
C++ Rvalue References Explained, by Thomas Becker[這篇文章的譯文在這裏]
Universal References in C++11, by Scott Meyers
A Brief Introduction to Rvalue References, by Howard E. Hinnant, Bjarne Stroustrup, and Bronek Kozicki