引子

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. [Luogu P3157][CQOI2011]动态逆序对 (树套树)

    题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的 ...

  2. Json Master masters JSON!

    对于一个软件开发人员, JSON 是最熟悉的东西之一了, 每一个开发人员基本上每一天都会跟 JSON 打交道. 作为一个大前端开发人员, 当看到从服务器返回的 JSON 数据时, 尤其是大数据量或者复 ...

  3. 面试 之 nginx,负载,动静分离

    大家先看这个逻辑图 为什么我们要这样去架构我们的一个项目呢? 这样做的话,动态请求要先访问 A,A 转发访问 B,再由 B 返回结果给 A,A 最后又将结果返回给客户端这样是不是很麻烦? 最初开发的时 ...

  4. JavaSE基础语法学习

    标识符 表示类名的标识符用大写字母开始. 表示方法和变量的标识符用小写字母开始,后面的描述性词以大写开始. 注意 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($).或者下划线(_)开始 ...

  5. git clone下载速度慢的解决方案

    由于自己碰到git clone速度慢的问题,查询后发现有一个很好用的方法 首先获得你git clone的原格式,例如: git clone https://github.com/graykode/nl ...

  6. MySql索引使用策略

    MySql索引 索引优点 1.可以通过建立唯一索引或者主键索引,保证数据的唯一性.2.提高检索的数据性能3.在表连接的连接条件 可以加速表与表直接的相连 4.建立索引,在查询中使用索引 可以提高性能 ...

  7. .Net Newtonsoft.Json 转json时将枚举转为字符串

    1:非列表类型枚举 [JsonConverter(typeof(StringEnumConverter))] public SubjectTypeEnum subject_type { get; se ...

  8. new与malloc的10点区别(转)

    1. 申请的内存所在位置 new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存.自由存储区是C++基于new操作符的一个抽象概念,凡是通过ne ...

  9. 解密Cookie,这一篇就够了

    一.Cookie介绍 因为HTTP协议是无状态的,每次请求都是独立的,服务器端无法判断两次请求是否来自同一个用户,进而也就无法判断用户的登录状态,也不知道用户上一次做了什么.所以Cookie就是用来绕 ...

  10. 深度学习论文翻译解析(十四):SSD: Single Shot MultiBox Detector

    论文标题:SSD: Single Shot MultiBox Detector 论文作者:Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Sz ...