Prefect Forward 实现

涉及到模板偏特化、万能引用、引用折叠、模板类型推导

1. Move reference

​ 利用模板偏特化,去除引用,保证返回一个不带引用的类型

template<typename T>
struct RemoveReference
{
    using type=T;
    RemoveReference()
    {
        std::cout<<"T"<<std::endl;
    }
};
template<typename T>
struct RemoveReference<T&>
{
    using type=T;
    RemoveReference()
    {
        std::cout<<"T&"<<std::endl;
    }
};
template<typename T>
struct RemoveReference<T&&>
{
    using type=T;
    RemoveReference()
    {
        std::cout<<"T&&"<<std::endl;
    }
};

2. Universal reference

&是左值引用,但&&一定是右值引用吗,答案是否定的!

T&& t

出现以上声明时,如果T是需要被推导出来的,比如模板,auto,那么t就不一定是右值引用了!其判断方法为引用折叠的规则

3. Reference Collapse

​ 声明引用的引用编译器不会通过,但可以间接声明,比如模板类型T本身可以隐含引用&,再加上函数参数里的引用&

​ 引用折叠发生的情况有template、auto、typedef、decltype

x& & -- x&//左值引用的左值引用等价于左值引用

x& && -- x&//左值引用的右值引用等价于左值引用

x&& & -- x&//右值引用的左值引用等价于左值引用

x&& && -- x&&//右值引用的右值引用等价于右值引用

总之任一个为左值引用,最终都是左值引用,反之为右值引用。

​ 例子:

int a=10;//a 左值
int &b=a;//b 左值引用
auto&& c=b;//然鹅,c是左值引用;
//auto被推导为int(reference-ness),而又因为b是左值,故被推导为int&,最终形式为int& &&,根据引用折叠,即c为左值引用

void fun1(int&&t){...}//t一定为右值引用

template<class T>
void fun2(T&&t){...}//T被推导为何类型是不定的,因此t不一定为右值引用

4 .Template type deduction

  1. 模板类型推导时,忽略引用类型参数的引用性(reference-ness)

  2. 给universal reference参数进行类型推导时,左值要特别对待(推导为Type&)

  3. 传值参数的类型推导,入参的诸如所有const /volatile的特性都会忽略

  4. 模板板类型推导过程中,数组或函数做微参数时会退化成指针,除非模板函数的参数是引用类型

5. move

move的实现主要有两点:

  1. 接收万能引用,保证任何值都能传递
  2. 去除引用,然后强制转换成右值引用
template<typename T>
typename RemoveReference<T>::type && myMove(T&& val)
{
    return static_cast<typename RemoveReference<T>::type&&>(val);
}

6. forward

​ 注意以下foo函数,如果传入的是左值,应该去调用左值引用类型的check函数,否则应该调用右值引用类型的check函数,这样便是完美转发

void check(int & val)
{
    std::cout<<"this is lval"<<std::endl;
}
void check(int && val)
{
    std::cout<<"this is rval"<<std::endl;
}

template<typename T>
void foo(T&& val)
{
    //val一定是左值
    std::cout<<"after forward:\n";
    //虽然val一定是左值,但T保存着val原本究竟是左值还是右值的信息,利用它实现完美转发
    check(myForward<T>(val));
}
...
foo(1);//rval
foo(myMove(x));//rval
foo(x);//lval

完美转发的实现依靠以下两点:

​ 1.注意到foo函数中的val本身一定是一个左值!它在完美转发中只起到传值的作用!

​ 2.val原本的类型信息:左值还是右值?它被保存在T中,需要利用它

//左值完美转发
template<typename T>
T&& myForward(typename RemoveReference<T>::type& lval)
{
    return static_cast<T&&>(lval);
}
//右值完美转发,注意到右值转发成一个左值是错误的
template<typename T>
T&& myForward(typename RemoveReference<T>::type&& rval)
{
    static_assert(!std::is_lvalue_reference<T>::value,"rval forward to lval!");
    return static_cast<T&&>(rval);
}

​ 以左值的完美转发为例,传入类型为自定义类型Type

  1. 如果外部函数foo传入的是一个右值,其形参T&&推导为Type&&

    由于T被推导为Type,因此调用forward<Type>后传出右值引用

  2. 如果外部函数foo传入的是一个左值,则推导为Type& &&,根据引用折叠原则,其形参T&&最终推导为Type &

    由于T被推导成Type&,因此调用forward<Type&>后传出左值引用


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

C++ PCH提升编译速度 下一篇