问题

The need for this function stems from the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references)

就是说右值引用本身其实是个左值。比如:

#include <stdio.h>
#include <utility>

int gettmp() {
    return 2;
}
void test(int &) 
{
    printf("call &\n");
}

void test(int &&)
{
    printf("call &&\n");
}

template<typename T>
void forwardtest(T &&t)
{
    // 执行到这里时 t 的类型是个左值
    test(t); // 输出 call &
}

int main()
{
    int && tmp = gettmp();
    tmp = 3; // tmp可以是左值,并对它赋值。
    test(tmp); // 输出 call&
    test(2); // 2 是个右值,输出call&&
    forwardtest(2);//2是个右值,但还是输出call&,因为在forwardtest里面t是个左值 
    return 0;
    int &a = tmp;
}

输出:
    call &
    call &&
	call &

如何让forwardtest中 t 的类型保留它原始的类型呢:如果t是左值就调用test时让t是左值,如果t是右值调用test让t是右值?

std::forward

主要作用就是得到变量原始的类型 ,把forwardtest中调用test(t)改为:test(std::forward(t))就可以了。

template<typename T>
void forwardtest(T &&t)
{
    test(std::forward<T>(t));
}

输出:
    call &
    call &&
    call &&

std::forward引用折叠

在forwardtest函数的类型推导过程中会用到引用折叠. 详细的可以查阅更多其它资料:effective modern c++ item 28,会讲std::forward怎么实现的以及引用折叠

T&   &  -> T&   
T&   && -> T&  
T&&  &  -> T&   
T&&  && -> T&&  

只有T的类型是T&&时, t的类型才是T&&, 其它的情况t的类型是T&.

下面是std::forward的实现

remove_reference_t不管理T的类型是&类弄还是&&类型,都把&去掉得到T的没有引用的类型,比如:

  • remove_reference_t<int&>得到 int
  • remove_reference_t<int&&>也会得到 int
template<typename T> // C++14; still in
T&& forward(remove_reference_t<T>& param) // namespace std
{
    return static_cast<T&&>(param);
}

比如当传入的参数是int& 得到如下推导,最后forward返回int&:

int& && forward(int & param) // namespace std
{
    return static_cast<int& &&>(param);
}
引用折叠后得到:
int& forward(int & param) // namespace std
{
    return static_cast<int&>(param);
}

当传入的参数是int&& 得到如下推导,最后forward返回int&&:

int&& && forward(int & param) // namespace std
{
    return static_cast<int&& &&>(param);
}
引用折叠后得到:
int&& forward(int & param) // namespace std
{
    return static_cast<int&&>(param);
}

参考

what-are-the-main-purposes-of-using-stdforward-and-which-problems-it-solves

use-of-stdforward-in-c++