美文网首页
std::forward和std::move源码分析

std::forward和std::move源码分析

作者: gykimo | 来源:发表于2022-01-05 19:36 被阅读0次

    原文:https://blog.csdn.net/kupepoem/article/details/119948044

    一、右值引用

    int和int&是什么?都是类型。int是整数类型,int&则是整数引用类型。同样int&&也是一个类型。两个引号&&是C++ 11提出的一个新的引用类型,右值引用类型,这是一个新的类型。如果你记住这个新类型,那么很多疑问都能迎刃而解。void G(A &&a) ,就很容易明白a是新类型右值引用的一个变量一个值,肯定是一个左值而不是右值。

    二、forward源码分析

     /**
       *  @brief  Forward an lvalue.
       *  @return The parameter cast to the specified type.
       *
       *  This function is used to implement "perfect forwarding".
       */
      template<typename _Tp>
        constexpr _Tp&&
        forward(typename std::remove_reference<_Tp>::type& __t) noexcept
        { return static_cast<_Tp&&>(__t); }
     
      /**
       *  @brief  Forward an rvalue.
       *  @return The parameter cast to the specified type.
       *
       *  This function is used to implement "perfect forwarding".
       */
      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);
        }
    

    对于forward函数

    std::forward<T>(u)有两个参数:T与 u。

    a. 当T为左值引用类型时,u将被转换为T类型的左值。比如T 为int&

    b. 否则u将被转换为T类型右值。比如T为 int ,int &&

    三、示例分析

    1、不使用 std::forward

    下述代码G不管传入什么类型的参数,只会最终调用 void F(int& a);

    using namespace std;
     
    void F(int& a) {
        cout << "int& version " <<a <<endl;
    }
     
    void F(int&& a) {
        // do something
        cout << "int&& version " <<a <<endl;
    }
     
    template<class A>
    void G(A &&a) {
        F(a); 
    }
     
    int main (int argc, char *argv[]) {
        int i = 2;
        G(i); //输出int& version
        G(5); //输出int& version
      return 0; 
    }
    

    G不管传入什么类型的参数,只会最终调用 void F(int& a)

    这是为什么?

    调用G(5)时,A被推导为int类型,但在G内部调用F(a)时,因为G函数中a是一个int &&的变量是个左值,调用F时编译译器自动推断调用 F(int & &&)类型的函数,根据引用折叠原理就会调用F(int & )c++引用折叠

    调用G(i)时,A被推导为int &类型,根据引用折叠原理就会调用F(int & )。

    2、用 std::forward

    用 std::forward时,G的最终调用出现差异化

    using namespace std;
     
    void F(int& a) {
        cout << "int& version " <<a <<endl;
    }
     
    void F(int&& a) {
        // do something
        cout << "int&& version " <<a <<endl;
    }
     
    template<class A>
    void G(A &&a) {
        F(std::forward<A>(a)); 
    }
     
    int main (int argc, char *argv[]) {
        int i = 2;
        G(i); //输出int& version
        G(5); //输出int&& version
     
      return 0; 
    }
    

    调用G(5)时,A被推导为int类型,但在G内部调用F(a)时,因为G函数中a是一个int &&的变量是个左值,调用std::forward<A>(a)(std::forward<int>(a)),A此时为int,强制转换为了右值。

    调用G(i)时,A被推导为int &类型,调用std::forward<A>(a)(std::forward<int&>(a)),还是强制转换成了左值。

    四、std::move源码

    头文件move.h

      template<typename _Tp>
        constexpr typename std::remove_reference<_Tp>::type&&
        move(_Tp&& __t) noexcept
        { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
    

    可以看出std::move也是进行了强制类型转换,把一个左值强制转换成右值,没有新奇之处,参数__t的类型是万能引用(CSDN),可以接受不同类型的参数。

    using namespace std;
     
    void F(int& a) {
        cout << "int& version " <<a <<endl;
    }
     
    void F(int&& a) {
        // do something
        cout << "int&& version " <<a <<endl;
    }
     
    template<class A>
    void G(A &&a) {
        F(std::forward<A>(a)); 
    }
     
    int main (int argc, char *argv[]) {
        int i = 2;
        G(std::move(i)); //输出int&& version
        G(5); //输出int&& version
     
      return 0; 
    }
    

    std::remove_reference

    remove_reference源码
    实现方式
    这里的实现非常简单,就是去掉了类型的左值引用和右值引用,直接返回类型的基本内容。

    性能开销
    是否只在编译期间出现不会有任何运行时开销:是的。

    /// remove_reference
    template<typename _Tp>
    struct remove_reference
    { typedef _Tp   type; };
    
    template<typename _Tp>
    struct remove_reference<_Tp&>
    { typedef _Tp   type; };
    
    template<typename _Tp>
    struct remove_reference < _Tp&& >
    { typedef _Tp   type; };
    

    相关文章

      网友评论

          本文标题:std::forward和std::move源码分析

          本文链接:https://www.haomeiwen.com/subject/cdskcrtx.html