C++11 模板参数的“右值引用”是转发引用吗分享!

在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 右值引用内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!

—-想了解C++11 模板参数的“右值引用”是转发引用吗分享!全部内容且更多的C语言教程关注<计算机技术网(www.ctvol.com)!!>

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/484946.html

(0)
上一篇 2020年11月10日
下一篇 2020年11月10日

精彩推荐