c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用
2020-11-30 23:22
1591 查看
引子
T&&在代码里并不总是右值引用:
void f(Widget&& param); // rvalue reference Widget&& 1044 ; 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 模板核心知识(六)—— 理解auto推导规则
- c++11-17 模板核心知识(三)—— 非类型模板参数 Nontype Template Parameters
- 区分综合引用(Universal reference)和右值引用
- 模板与c++11--右值引用
- C++11中的universal引用和右值引用
- C++11:利用模板简化重载右值引用参数的函数
- Item 24: 区分右值引用和universal引用
- item 24: 区分右值引用和universal引用
- Item 24: 区分右值引用和universal引用
- 杂货边角(17):C++11的右值引用和移动语义
- C++11中的右值引用及move语义编程
- 14、【C++】C++11新特性:类的新特性/移动语义和右值引用/包装器
- C++11之右值引用(一):从左值右值到右值引用
- 对C++11中的`移动语义`与`右值引用`的介绍与讨论
- C++11之右值引用(二):右值引用与移动语义
- C++11的右值引用的具体使用
- 如何评价 C++11 的右值引用(Rvalue reference)特性
- C++11 新特性之右值引用与转移语义
- C++11新特性: 右值引用 和 右值语义(rvalue sematics)
- C++11 标准新特性: 右值引用与转移语义