c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用
引子
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)和右值引用的更多相关文章
- c++11-17 模板核心知识(五)—— 理解模板参数推导规则
Case 1 : ParamType是一个指针或者引用,但不是universal reference T& const T& T* Case 2 : ParamType是Univers ...
- [转载] C++11中的右值引用
C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...
- C++11中的右值引用
原文出处:http://kuring.me/post/cpp11_right_reference May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移 ...
- item 24: 区分右值引用和universal引用
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 古人曾说事情的真相会让你觉得很自在,但是在适当的情 ...
- C++11左值引用和右值引用
转载:https://www.cnblogs.com/golaxy/p/9212897.html C++11的左值引用与右值引用总结 概念 1.&与&& 对于在C++中,大家 ...
- C++11标准之右值引用(rvalue reference)
1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包 ...
- 【C/C++开发】C++11:右值引用和转发型引用
右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如 ...
- [c++11]右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- [转][c++11]我理解的右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
随机推荐
- [Luogu P3157][CQOI2011]动态逆序对 (树套树)
题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的 ...
- Json Master masters JSON!
对于一个软件开发人员, JSON 是最熟悉的东西之一了, 每一个开发人员基本上每一天都会跟 JSON 打交道. 作为一个大前端开发人员, 当看到从服务器返回的 JSON 数据时, 尤其是大数据量或者复 ...
- 面试 之 nginx,负载,动静分离
大家先看这个逻辑图 为什么我们要这样去架构我们的一个项目呢? 这样做的话,动态请求要先访问 A,A 转发访问 B,再由 B 返回结果给 A,A 最后又将结果返回给客户端这样是不是很麻烦? 最初开发的时 ...
- JavaSE基础语法学习
标识符 表示类名的标识符用大写字母开始. 表示方法和变量的标识符用小写字母开始,后面的描述性词以大写开始. 注意 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($).或者下划线(_)开始 ...
- git clone下载速度慢的解决方案
由于自己碰到git clone速度慢的问题,查询后发现有一个很好用的方法 首先获得你git clone的原格式,例如: git clone https://github.com/graykode/nl ...
- MySql索引使用策略
MySql索引 索引优点 1.可以通过建立唯一索引或者主键索引,保证数据的唯一性.2.提高检索的数据性能3.在表连接的连接条件 可以加速表与表直接的相连 4.建立索引,在查询中使用索引 可以提高性能 ...
- .Net Newtonsoft.Json 转json时将枚举转为字符串
1:非列表类型枚举 [JsonConverter(typeof(StringEnumConverter))] public SubjectTypeEnum subject_type { get; se ...
- new与malloc的10点区别(转)
1. 申请的内存所在位置 new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存.自由存储区是C++基于new操作符的一个抽象概念,凡是通过ne ...
- 解密Cookie,这一篇就够了
一.Cookie介绍 因为HTTP协议是无状态的,每次请求都是独立的,服务器端无法判断两次请求是否来自同一个用户,进而也就无法判断用户的登录状态,也不知道用户上一次做了什么.所以Cookie就是用来绕 ...
- 深度学习论文翻译解析(十四):SSD: Single Shot MultiBox Detector
论文标题:SSD: Single Shot MultiBox Detector 论文作者:Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Sz ...