引子

T&&在代码里并不总是右值引用:

void f(Widget&& param);      // rvalue reference

Widget&& var1 = Widget();      // rvalue reference

auto&& var2 = var1;        // not rvalue reference

template<typename T>
void f(std::vector<T>&& param); // rvalue reference template<typename T>
void f(T&& param); // not rvalue reference

T&&代表两种含义:

  • 右值引用
  • 万能引用(universal references, or forwarding references)

如何区分

万能引用一般出现在两个场景中:

  • 模板参数
template<typename T>
void f(T&& param); // param is a universal reference
  • auto声明
auto&& var2 = var1; // var2 is a universal reference

我们分别讨论下这两种场景。

模板参数

我们注意到,涉及到万能引用的地方,都会有参数推导的过程,例如上面的T和var2. 而右值引用则没有这个过程:

void f(Widget&& param);        // no type deduction; param is an rvalue reference

Widget&& var1 = Widget();     // no type deduction; var1 is an rvalue reference

但是即使语句设计到参数推导,也不一定就是万能引用。例如:

template<typename T>
void f(std::vector<T>&& param); // param is an rvalue reference std::vector<int> v;
f(v); // error! can't bind lvalue to rvalue reference

这点还是比较好理解的。万能引用需要依靠表达式来初始化自己是右值引用还是左值引用,但是上面这个例子没有表现出这一点,它仅仅是推断了T的类型,但是param的类型一直都是std::vector<T>&&

我们再举一个vector中的例子:

template<class T, class Allocator = allocator<T>>
class vector {
public: void push_back(T&& x); // rvalue reference template <class... Args>
void emplace_back(Args&&... args); // universal reference
};
  • push_back(T&& x)中的T&&为右值引用,因为这个虽然是T&&,但是不涉及到参数推导。当push_back被instantiated时,实际的调用类似于:
std::vector<Widget> v;

...
class vector<Widget, allocator<Widget>> {
public:
void push_back(Widget&& x); // rvalue reference

};

可以很明显的看出此时没有参数推导的过程。

  • template <class... Args> emplace_back(Args&&... args)中的Args&&为万能引用。Args与T是相互独立的,所以Args有一个独立的参数推断过程。

const disqualify universal reference

有意思的是,当参数加上const后,就一定是右值引用:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&); int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which would bind an rvalue reference to an lvalue

至于为什么会有这个规定,按照Why adding const makes the universal reference as rvalue的说法,大体有两点原因:

  • const T&&允许你重载一个函数模板,它只接受右值引用。如果const T&&也被当做universal reference,那么将没有办法让函数只接受右值引用。
  • 显示禁用某个函数接受右值引用:template <typename T> void cref(const T&&) = delete;

auto声明

对于auto的场景来说,所有的auto&&都是万能引用,因为它总是有参数推导的过程。例如定义一个记录函数执行时间的lambda(C++14中允许使用auto来声明lambda的函数):

auto timeFuncInvocation = [](auto &&func, auto &&... params) {
start timer;
std::forward<decltype(func)>(func)( // invoke func
std::forward<decltype(params)>(params)... // on params
);
stop timer and record elapsed time;
};

(完)

朋友们可以关注下我的公众号,获得最及时的更新:

c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用的更多相关文章

  1. c++11-17 模板核心知识(五)—— 理解模板参数推导规则

    Case 1 : ParamType是一个指针或者引用,但不是universal reference T& const T& T* Case 2 : ParamType是Univers ...

  2. [转载] C++11中的右值引用

    C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...

  3. C++11中的右值引用

    原文出处:http://kuring.me/post/cpp11_right_reference May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移 ...

  4. item 24: 区分右值引用和universal引用

    本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 古人曾说事情的真相会让你觉得很自在,但是在适当的情 ...

  5. C++11左值引用和右值引用

    转载:https://www.cnblogs.com/golaxy/p/9212897.html C++11的左值引用与右值引用总结 概念 1.&与&&  对于在C++中,大家 ...

  6. C++11标准之右值引用(rvalue reference)

    1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包 ...

  7. 【C/C++开发】C++11:右值引用和转发型引用

    右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如 ...

  8. [c++11]右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  9. [转][c++11]我理解的右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

随机推荐

  1. 03 . Gin+Vue开发一个线上外卖应用(用户数据创建,插入,跨域处理)

    功能和背景介绍 在项目的登录功能中,如果在登录时发现用户名和密码在用户表中不存在,会自动将用户名和密码保存在用户表中,创建一个新的用户. 因此,除了使用手机号和验证码登录以外,还支持使用用户名.密码进 ...

  2. php socket通信的简单实现

    socket通信的原理在这里就不说了,它的用途还是比较广泛的,我们可以使用socket来做一个API接口出来,也可以使用socket来实现两个程序之间的通信,我们来研究一下在php里面如何实现sock ...

  3. vue项目优化与上线

    一.项目优化策略 1.生成打包报告 2.第三方库启用CDN 3.Element-ui组件按需加载 4.首页内容定制 5.路由懒加载 1.生成打包报告 1.1通过vue-cli的UI面板直接查看 1.2 ...

  4. python数据类型之dict(字典)

    dict字典 关注公众号"轻松学编程"了解更多. 1.概述 dict也是一种存储方式,类似于list和tuple,但是,字典采用键-值(key-value)的形式存储. 优点:具有 ...

  5. python使用redis缓存数据库

    Redis 关注公众号"轻松学编程"了解更多. Windows下直接解压可用,链接:https://pan.baidu.com/s/1rD4ujoN7h96TtHSu3sN_hA ...

  6. 深入研究Paxos算法原理

    一.Paxos算法产生的背景 Paxos算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一,其解决的问题就是在分布式系统中如何就某个值(决议)达成一致. ...

  7. Oracle终极彻底卸载

    停止使用Oracle的服务 停用oracle服务,进入计算机管理,在服务中,找到oracle开头的所有服务,右击选择停止. 运行卸载Oracle数据库程序 一般情况运行Oracle自带的卸载程序,如使 ...

  8. pycharm新建项目时选择virtualenv的说明

    虚拟环境及venv和virtualenv介绍:https://www.cnblogs.com/mind18/p/13877170.html pip介绍:https://www.cnblogs.com/ ...

  9. Java入门(6)

    阅读书目:Java入门经典(第7版) 作者:罗格斯·卡登海德 当方法在子类和超类都定义了时,将使用子类的定义:因此子类可以修改,替换或完全删除超类的行为或属性. 关键字super引用对象的上一级超类, ...

  10. NoSQL数据库的四大分类的分析

    分类 Examples举例 典型应用场景 数据模型 优点 缺点 键值(key-value) Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB 内容缓 ...