std::move和std::forward仅仅是执行转换(cast)的函数(事实上是函数模板)。std::move无条件的将它的实参转换为右值,而std::forward只在特定情况满足时下进行转换。

std::move

template <class _Ty>
constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
    return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}

std::move函数模板返回一个remove_reference_t<_Ty>&&,确保返回的一定是右值。

std::forward

template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept {
    return static_cast<_Ty&&>(_Arg);
}

template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept {
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

std::forward函数模板使用时显式指明模板参数类型,根据条件进行类型转换。

std::forward完美转发

void process(Widget& lval) {
    std::cout << "左值process" << std::endl;
}
void process(Widget&& lval) {
    std::cout << "右值process" << std::endl;
}
template<typename T>
void logAndProcess(T&& param) {
    process(temp::forward<T>(param));
}
Widget w;
logAndProcess(w);
logAndProcess(Widget());

logAndProcess模板函数,使用通用引用的形式进行模板类型推导,如果传入左值,TParam都会被推导为左值引用;如果传入右值,就正常推导,即T推导为Widget,param推导为Widget&&。

所以logAndProcess(w)函数体中,T为Widget&,param为Widget&,forward<T>(param)会调用forward(remove_reference_t<_Ty>& _Arg)版本的重载函数,返回static_cast<_Ty&&>(_Arg),根据引用折叠的规则,注意这里的_Ty为Widget&,Widget& && 就是Widget&,返回左值引用。

logAndProcess(Widget())函数体中,T为Widget,param为Widget&&,forward<T>(param)会调用forward(remove_reference_t<_Ty>&& _Arg)版本的重载函数,返回static_cast<_Ty&&>(_Arg),_Ty为Widget,所以返回Widget&&,即右值引用。

std::forward<Widget>(w) 和 std:forward<Widget&>(w)

std::forward<Widget>(w)返回右值,std:forward<Widget&>(w)返回左值,他们两个都会调用forward(remove_reference_t<_Ty>& Arg)版本的重载函数,因为_Ty类型的不同和引用折叠,一个返回右值,一个返回左值。

参考文章:https://cntransgroup.github.io/EffectiveModernCppChinese/5.RR...


月球上的柑橘
1 声望0 粉丝