为什么我的 forward_ 函数对右值不起作用?

Why doesn't my forward_ function work for rvalues?

我已经了解 std::move 的工作原理并实现了我自己的版本,仅供练习。现在我想了解 std::forward 是如何工作的:

到目前为止我已经实现了这个:

#include <iostream>


template <typename T>
T&& forward_(T&& x)
{
    return static_cast<T&&>(x);
}


/*template <typename T>
T&& forward_(T& x)
{
    return static_cast<T&&>(x);
}*/

void incr(int& i)
{
    ++i;
}

void incr2(int x)
{
    ++x;
}

void incr3(int&& x)
{
    ++x;
}

template <typename T, typename F>
void call(T&& a, F func)
{
    func(forward_<T>(a));
}


int main()
{

    int i = 10;
    std::cout << i << '\n';
    call(i, incr);
    std::cout << i << '\n';

    call(i, incr2);
    std::cout << i << '\n';

    call(0, incr3); // Error: cannot bind rvalue reference of type int&& to lvalue of type int.

    std::cout << "\ndone!\n";
}

为什么我必须提供采用左值引用的重载 forward(T&) 版本?据我了解,转发引用可以根据其参数的类型产生左值或右值。因此,将纯右值文字 0 与采用类型 int&& 的右值引用的 incr3 函数一起传递给 call 通常不需要 forward<T>(T&)?!

如果您手动指定 T(而不是让编译器推断它),T&& 引用将不再是转发引用。如果 T 不是左值引用,则 T&& 是右值引用并且不接受左值。

例如,如果你做forward_<int>(...),那么参数是一个右值引用,...只能是一个右值。

但是如果你这样做forward_(...),那么这个参数是一个转发引用并且...可以有任何值类别。 (虽然这样称呼它是没有意义的,因为 forward_(x) 将具有与 x 本身相同的值类别。)

很明显你在想为什么有两个版本的std::forward;一个采用对类型参数 T& 的左值引用,另一个采用对类型参数的通用引用(转发)。 T&&.

在您的情况下,您正在使用函数模板 call 内部的 forward_,该模板也具有转发引用。问题在于,即使 call 函数使用右值调用,它也始终使用 forward_ 作为左值,因为 call 无法在没有对象(参数)的情况下传递其参数。请记住,对象的名称是一个 lvlaue,即使它是从右值初始化的。这就是为什么在您的示例中总是调用 forward_(T&) 的原因。

  • 现在你问为什么有二版转发引用? 它非常简单,您可能已经猜到了:它用于 r 值(值而不是那些对象的名称)。

这是一个例子:

template <typename T>
T&& forward_(T& x)
{
    std::cout << "forward_(T&)\n";
    return static_cast<T&&>(x);
}

template <typename T>
T&& forward_(T&& x)
{
    std::cout << "forward_(T&&)\n";
    return static_cast<T&&>(x);
}

int main()
{

    int i = 10;
    forward_(i); // forward(T&)  (1)
    forward_(5); // forward(T&&) (2)
    forward_("Hi"); // forward(T&) (3)
}