c++11的move forward(一)源码分析

一. 模板类型推断

参考:https://blog.youkuaiyun.com/big_yellow_duck/article/details/52140262

template <typename T>
void f(ParamType param);

这里面几个主要的点:
1:通用引用(universal references) type&&

If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
如果一个变量或者参数被声明为T&&,其中T是被推导的类型,那这个变量或者参数就是一个universal reference。

2:引用类型或者通用引用类型,T和ParamType推导类型有差别。

3: 如果ParamType既不是引用也不是指针类型

template <typename T>
void f(T param);    // 此处会有拷贝(构造)

如果传入的参数expr是引用,那么引用会被忽略
如果传入的参数expr有const,volatile修饰符也会被忽略

模板的定义是值传递,所以忽略引用。而且因为是值传递,传入的参数是const,并不意味着他们的拷贝也是const。

4:函数作为参数
如果ParamType是值语义,则其类型推导为函数指针。
如果ParamType是引用语义,则其类型推导为函数引用。
实际使用中区别不大。

二.引用折叠

​ X& & <=> X&, 左值引用的左值引用等价于左值引用 ​
X& && <=> X&, 左值引用的右值引用等价于左值引用 ​
X&& & <=> X&, 右值引用的左值引用等价于左值引用 ​
X&& && <=> X&&,右值引用的右值引用等价于右值引用

三.重载模板函数调用优先顺序

template<class T>
void func(T&& t)
{
    std::cout << "void func1(T&& t)" << std::endl;
}

template<class T>
void func(const T& t)
{
    std::cout << "void func1(T& t)" << std::endl;
}

考虑以上两个重载函数的区别。

func(5); // 调用 T&&
int a = 5;
func(a) // 调用T&&
const int b = 5;
func(a) // 调用T&

以上的几种调用情况,如果只有T&&版本,则都调用T&&版本。如果只有T&版本,则都调用T&版本。
在这两种版本重载的情况下,则按照匹配度更高的优先。
根据实验结果总结一下:

  1. 如果是const类型的变量,则优先匹配const的重载版本。const修饰右值一般不存在。
  2. 如果上一条区分不出,则左值优先匹配左值版本,右值优先匹配右值版本。

remove_reference

去除类型的引用,将引用类型转化为非引用类型

template <class T>
struct remove_reference {
    using type = T;
};

// 左值引用版本
template <class T>
struct remove_reference<T&> {
    using type = T;
};

// 右值引用版本
template <class T>
struct remove_reference<T&&> {
    using type = T;
};

move

template <class T>
typename remove_reference<T>::type&& move(T&& t) noexcept {
        using return_type = typename remove_reference<T>::type&&;
        return static_cast<return_type>(t);
    }

举个栗子说明:

int a = 0;
int& b = a;
move(b);

根据模板类型推导
T => int&
remove_reference::type => int
return_type => int&&

所以move函数只是做了类型转换,在运行阶段并不做任何事情。
右值会成为可移动的候选者,因此对一个对象使用std::move是告诉编译器,这个对象符合被移动的条件。那就是为什么std::move会有这个名字:很容易指出可能被移动的对象。

forward

std::forward能将一个参数原封不动(包括参数值、左/右值等)地传递给另一个函数,这叫完美转发。

template <class T>
T&& forward(typename remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

template <class T>
T&& forward(typename remove_reference<T>::type&& t) noexcept {
    return static_cast<T&&>(t);
}

对右值引用使用std::move,对通用引用使用std::forward

右值引用和通用引用参数在函数中会默认转化为左值引用,所以转发给其他函数时,需要转换为我们希望的参数类型。
简而言之,当把右值引用转发给其他函数时,右值引用应该无条件转换为右值(借助std::move),因为右值引用总是绑定右值。而当把通用引用转发给其他函数时,通用引用应该有条件地转换为右值(借助std::forward),因为通用引用只是有时候会绑定右值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值