cannot convert ‘a’ (type ‘int’) to type ‘int&&’

背景

啓用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後編譯出錯了?

從出錯信息裏已經能夠看到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的類型是Aip

引用摺疊規則

根據上一條規則,當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

相關文章
相關標籤/搜索