时间:2021-05-20
在C++11中,&&不再只有逻辑与的含义,还可能是右值引用:
void f(int&& i);但也不尽然,&&还可能是转发引用:
template<typename T>void g(T&& obj);“转发引用”(forwarding reference)旧称“通用引用”(universal reference),它的“通用”之处在于你可以拿一个左值绑定给转发引用,但不能给右值引用:
void f(int&& i) { }template<typename T>void g(T&& obj) { }int main(){ int n = 2; f(1);// f(n); // error g(1); g(n);}一个函数的参数要想成为转发引用,必须满足:
换言之,以下函数的参数都不是转发引用:
template<typename T>void f(const T&&);template<typename T>void g(typename std::remove_reference<T>&&);template<typename T>class A{ template<typename U> void h(T&&, const U&);};另一种情况是auto&&变量也可以成为转发引用:
auto&& vec = foo();所以写范围for循环的最好方法是用auto&&:
std::vector<int> vec;for (auto&& i : vec){ // ...}有一个例外,当auto&&右边是初始化列表,如auto&& l = {1, 2, 3};时,该变量为std::initializer_list<int>&&类型。
转发引用,是用来转发的。只有当你的意图是转发参数时,才写转发引用T&&,否则最好把const T&和T&&写成重载(如果需要的话还可以写T&,还有不常用的const T&&;其中T是具体类型而非模板参数)。
转发一个转发引用需要用std::forward,定义在<utility>中:
调用g有几种可能的参数:
你也许会疑惑,为什么std::move不需要<T>而std::forward需要呢?这得从std::forward的签名说起:
template<typename T>constexpr T&& forward(std::remove_reference_t<T>&) noexcept;template<typename T>constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;调用std::forward时,编译器无法根据std::remove_reference_t<T>反推出T,从而实例化函数模板,因此<T>需要手动指明。
但是这并没有从根本上回答问题,或者可以进一步引出新的问题——为什么std::forward的参数不定义成T&&呢?
原因很简单,T&&会把T&、const T&、T&&和const T&&(以及对应的volatile)都吃掉,有了T&&以后,再写T&也没用。
且慢,T&&参数在传入函数是会匹配到T&&吗?
#include <iostream>#include <utility>void foo(int&){ std::cout << "int&" << std::endl;}void foo(const int&){ std::cout << "const int&" << std::endl;}void foo(int&&){ std::cout << "int&&" << std::endl;}void bar(int&& i){ foo(i);}int main(){ int i; bar(std::move(i));}不会!程序输出int&。在函数bar中,i是一个左值,其类型为int的右值引用。更直接一点,它有名字,所以它是左值。
因此,如果std::forward没有手动指定的模板参数,它将不能区分T&和T&&——那将是“糟糕转发”,而不是“完美转发”了。
最后分析一下std::forward的实现,以下代码来自libstdc++:
template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); }template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument" " substituting _Tp is an lvalue reference type"); return static_cast<_Tp&&>(__t); }综上,std::forward能完美转发。
程序员总是要在Stack Overflow上撞撞墙才能学会一点东西。
到此这篇关于C++11 模板参数的“右值引用”是转发引用吗的文章就介绍到这了,更多相关C++11 右值引用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
C++11引入了std::move语义、右值引用、移动构造和完美转发这些特性。由于这部分篇幅比较长,分为3篇来进行阐述。在了解这些特性之前,我们先来引入一些问题
C++11关于模板有一些细节的改进:模板的右尖括号模板的别名函数模板的默认模板参数模板的右尖括号C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,
右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一。从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题。
值与引用参数之间的区别:在C#中,既可以通过值也可以通过引用传递参数。在调用环境中通过引用传递参数允许函数成员(方法、属性、索引器、运算符和构造函数)更改参数的
C++引用—临时变量、引用参数和const引用如果实参与引用参数不匹配,C++将生成临时变量。如果引用参数是const,则编译器在下面两种情况下生成临时变量:实