C++11模板参数的“右值引用”是不是转发引用
小编这次要给大家分享的是C++11模板参数的“右值引用”是不是转发引用,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。
创新互联专注于南涧网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供南涧营销型网站建设,南涧网站制作、南涧网页设计、南涧网站官网定制、微信小程序开发服务,打造南涧网络公司原创品牌,更为您提供南涧网站排名全网营销落地服务。
在C++11中,&&不再只有逻辑与的含义,还可能是右值引用:
void f(int&& i);
但也不尽然,&&还可能是转发引用:
templatevoid g(T&& obj);
“转发引用”(forwarding reference)旧称“通用引用”(universal reference),它的“通用”之处在于你可以拿一个左值绑定给转发引用,但不能给右值引用:
void f(int&& i) { } templatevoid g(T&& obj) { } int main() { int n = 2; f(1); // f(n); // error g(1); g(n); }
一个函数的参数要想成为转发引用,必须满足:
- 参数类型为T&&,没有const或volatile;
- T必须是该函数的模板参数。
换言之,以下函数的参数都不是转发引用:
templatevoid f(const T&&); template void g(typename std::remove_reference &&); template class A { template void h(T&&, const U&); };
另一种情况是auto&&变量也可以成为转发引用:
auto&& vec = foo();
所以写范围for循环的最好方法是用auto&&:
std::vectorvec; for (auto&& i : vec) { // ... }
有一个例外,当auto&&右边是初始化列表,如auto&& l = {1, 2, 3};时,该变量为std::initializer_list
转发引用,是用来转发的。只有当你的意图是转发参数时,才写转发引用T&&,否则最好把const T&和T&&写成重载(如果需要的话还可以写T&,还有不常用的const T&&;其中T是具体类型而非模板参数)。
转发一个转发引用需要用std::forward,定义在
调用g有几种可能的参数:
- int i = 1; g(i);,T为int&,调用g(int&);
- const int j = 2; g(j);,T为const int&,调用g(const int&);
- int k = 3; g(std::move(k));或g(4);,T为int(不是int&&哦!),调用g(int&&)。
你也许会疑惑,为什么std::move不需要
templateconstexpr T&& forward(std::remove_reference_t &) noexcept; template constexpr T&& forward(std::remove_reference_t &&) noexcept;
调用std::forward时,编译器无法根据std::remove_reference_t
但是这并没有从根本上回答问题,或者可以进一步引出新的问题——为什么std::forward的参数不定义成T&&呢?
原因很简单,T&&会把T&、const T&、T&&和const T&&(以及对应的volatile)都吃掉,有了T&&以后,再写T&也没用。
且慢,T&&参数在传入函数是会匹配到T&&吗?
#include#include 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++:
templateconstexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); } template 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); }
- 当转发引用T&& obj绑定左值int&时,匹配第一个重载,_Tp即T为int&,返回类型_Tp&&为int&(引用折叠:& &、& &&、&& &都折叠为&,只有&& &&折叠为&&);
- const int&同理;
- 当转发引用绑定右值int&&时,匹配第二个重载,_Tp为int,返回类型为int&&;
- const int&&同理。
综上,std::forward能完美转发。
看完这篇关于C++11模板参数的“右值引用”是不是转发引用的文章,如果觉得文章内容写得不错的话,可以把它分享出去给更多人看到。
当前标题:C++11模板参数的“右值引用”是不是转发引用
本文来源:http://myzitong.com/article/jhhegj.html