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++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
随机推荐
- OpenCascade拓扑对象之:拓扑对象方向继承关系
@font-face { font-family: "Times New Roman" } @font-face { font-family: "宋体" } @ ...
- testNG优雅的使用注解让你的测试项目开发更高效!
testNG大部分是通过xml配置测试类和监听类 但是这种方法就像传统的spring框架一样需要引入大量的xml配置信息,而且在各层之间也需要通过new对象传递.如果testNG能使用注解注入bean ...
- Django之富文本(获取内容,设置内容)
富文本 1.Rich Text Format(RTF) 微软开发的跨平台文档格式,大多数的文字处理软件都能读取和保存RTF文档,其实就是可以添加样式的文档,和HTML有很多相似的地方 图示 2.tin ...
- IOCP 模型1
// IOCP.cpp : Defines the entry point for the console application. // // #include "stdafx.h&quo ...
- 漫谈:从APP崩溃率标准,到Monkey介绍拓展Maxim,及Jenkins自动化配置,持续集成获取崩溃monkey日志
漫谈:从APP崩溃率标准,到Monkey介绍拓展Maxim,及Jenkins自动化配置,持续集成获取崩溃日志 1.APP崩溃率标准 开发或测试app的同学,对于app崩溃肯定非常熟悉,频繁的线上崩溃属 ...
- ATcoder Grand Contest总结
最前面: AT的题都很有思维难度,总结一下一些AT的常规操作 1.对于有操作的题目,如果正面推不行的话考虑倒推,将操作转化,寻找更好的性质 2.模型转化,看到某一种的计算的式子,需要考虑有没有更简化的 ...
- JS--遍历对象属性的五种方式
ES6 一共有 5 种方法可以遍历对象的属性. (1)for...in for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性). (2)Object.keys(obj) Ob ...
- 执行 yarn init报错,如何解决?
安装yarn以后执行yarn init 命令来初始化项目 报错如下所示: 解决方法: 1.先用npm init初始化项目 在初始化的最后一步 is this ok(yes)? 输入yes回车后,可能会 ...
- JS缓冲运动案例
点击"向右"按钮,红色的#red区块开始向右缓冲运动,抵达到黑色竖线位置自动停止,再次点击"向右"#red区块也不会再运动.点击"向左"按钮 ...
- 如何开发一个maven插件
maven是当下最流行的项目管理工具,其丰富的插件为我们的工作带来了很大的便利. 但是在一些情况下,开源的插件并不能完全满足我们的需求,我们需要自己创建插件,本文就从0开始带大家一起创建自己的插件. ...