美文网首页
move与forward详解

move与forward详解

作者: advanced_slowly | 来源:发表于2019-05-20 20:52 被阅读0次

    move与forward内部实现分析

    为了减少不必要的昂贵的拷贝代价,c++11提出了move semantic(移动语义),直接将右值(例如一些临时对象)的资源“偷”过来,从而避免了不必要的拷贝,提高了程序的执行效率。

    在utility头文件下定义了move和forward函数模板,在分析之前先看三个结构体模板remove_reference

    // STRUCT TEMPLATE remove_reference
    //泛型模板
    template <class _Ty>
    struct remove_reference { // remove reference
        using type = _Ty;
    };
    
    //模板的特化,不是偏特化
    template <class _Ty>
    struct remove_reference<_Ty&> { // remove reference
        using type = _Ty;
    };
    
    //模板的特化,不是偏特化
    template <class _Ty>
    struct remove_reference<_Ty&&> { // remove rvalue reference
        using type = _Ty;
    };
    

    从上诉三个类模板可以得到的信息为type是待推导数据类型_Ty的别名,并且_Ty的数据类型是去除引用(包括左值引用和右值引用)后的。

    一个宏定义remove_reference_t:

    template <class _Ty>
    using remove_reference_t = typename remove_reference<_Ty>::type;
    

    从上诉宏定义可见,remove_reference也是待推导类型_Ty的别名

    move函数模板源代码:

    // FUNCTION TEMPLATE move
    template <class _Ty>
    _NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
        return static_cast<remove_reference_t<_Ty>&&>(_Arg);
    }
    

    依据前一节模板类型推导的知识,当调用move函数时,函数模板类型推导需要推导_Ty和形式参数_Arg的数据类型。因move函数模板的参数类型为universal reference,所以_Arg的参数类型只能被推倒为以下几种:const左值引用(保留实参的const特性下),左值引用,右值引用。但不管_Arg被推倒为哪一种,它都将会通过类型转换转换为右值引用类型。因为有这三个结构体模板remove_reference发挥着作用。当调用move函数模板的实参是右值引用时,_Ty被推导为int,此时第一个remove_reference发挥着作用。当调用move函数模板的实参是左值引用时,_Ty被推导为int &,此时第二个remove_reference发挥着作用。

    举个简单的例子(这里以内置类型只做简单例子,实际意义不大):

    # include<iostream>
    
    void printI(int&& i)
    {
        std::cout << i << std::endl;
    }
    int main()
    {
        int a = 100;
    
        printI(std::move(a));   //a为左值,打印i的值为100
        printI(std::move(100)); //100为右值,打印i的值为100
    
        return 0;
    }
    
    
    # include <iostream>
    # include <string>
    
    class Example0
    {
    private:
        int x;
    public:
        Example0(int x = 0):x(x){}
    
        Example0(const Example0 & ex):x(ex.x)
        {
            std::cout << "The copy constructor are called" << std::endl;
        }
    
        Example0(Example0 && ex):x(ex.x)
        {
            ex.x = 0;
            std::cout << "The move constructor are called" << std::endl;
        }
    };
    
    class Example1
    {
    private:
        Example0 ex;
    public:
        Example1(const Example0 e) :ex(std::move(e))
        {
    
        }
    
        /*
        Example1( Example0 e) :ex(std::move(e))
        {
    
        }
        */
    
    };
    
    int main()
    {
        Example0 ex0(0);
    
        Example1 ex1(ex0);
    
        return 0;
    }
    

    上诉例子中Example1构造函数的参数有无const将是两个不一样的结果:有const,调用两次copy constructor。无const,调用一次copy constructor和一次move constructor。为什么有const会是两次调用copy constructor?

    在下面这段代码中

    Example1(const Example0 e) :ex(std::move(e))
     {
    
     }
    

    e确实被move无条件的转化为了右值,但是编译器在根据无条件转化后的右值实参匹配调用构造函数时,由于move constructor的形参是一个不带const的右值引用,所以还是会调用copy constructor。

    forward函数模板源码:

    // FUNCTION TEMPLATE forward
    template <class _Ty>
    _NODISCARD constexpr _Ty&& forward(
        remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
        return static_cast<_Ty&&>(_Arg);
    }
    
    template <class _Ty>
    _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
        static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
        return static_cast<_Ty&&>(_Arg);
    }
    

    第一个forward function template中,模板参数是一个左值。当调用该函数的实参为左值时,这个template就将forward an lvalue as lvalue.当调用该函数的实参为右值时,这个template就将forward an lvalue as rvalue.

    第二个forward function template中,模板参数是一个右值。当调用该函数的实参为左值时,这个template就将执行static_assert这一句,不进行转换。当调用该函数的实参为右值时,这个template就将forward an rvalue as rvalue.

    forward的作用将一个对象转发给另一个对象,同时保留该对象的左值性或右值性。

    注:以上源码来自vs2019,不同编译器,实作可能不同,例如上诉源码所在头文件可能不同,move的内部实现可能不同。

    相关文章

      网友评论

          本文标题:move与forward详解

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