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

背景

启用C++11编译老代码的时候,g++报错。示例代码如下:

#include <utility>

int main() {
  int a = 0;
  auto b = std::make_pair<int, int>(a, 1);

  return 0;
}

出错信息:

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实例化为:

std::make_pair(int&&, int&&);

std::make_pair接受两个int类型的右值,由于变量a不是右值,于是报错。就比如下面这条语句不能编译通过一样:

int &&b = a; // error: cannot bind ‘int’ lvalue to ‘int&&’

事实上,示例代码里并不是std::make_pair的正确用法,正确的用法是无需指定类型,让编译器自动推导,即:

auto b = std::make_pair(a, 1);

那为什么这样就能编译通过呢?

右值引用的两个规则

模板参数的推导规则

若模板函数的参数类型为右值引用,如:

template<typename T>
void foo(T&&);

那么T的推导规则为:当foo的实参是A类型的左值时,T的类型是A&;当foo的是实参是A类型的右值时,T的类型是A

引用折叠规则

根据上一条规则,当foo的实参为A类型的左值时,foo将变成:

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

转载于:https://my.oschina.net/u/149008/blog/387022

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值