转发引用和参数推导

Forwarding reference and argument deduction

我想深入了解一下完美转发,遇到了一个我自己也想不通的问题。 假设这段代码:

void fun(int& i) {
  std::cout << "int&" << std::endl;
}

void fun(int&& i) {
  std::cout << "int&&" << std::endl;
}

template <typename T>
void wrapper(T&& i) {
  fun(i);
}

int main()
{
    wrapper(4);
}

它打印 int&。要解决这个问题,应该使用 std::forward。这很清楚。不清楚的是为什么会这样。 上面的代码展开后是:

void fun(int & i)
{
  std::operator<<(std::cout, "int&").operator<<(std::endl);
}


void fun(int && i)
{
  std::operator<<(std::cout, "int&&").operator<<(std::endl);
}


template <typename T>
void wrapper(T&& i) {
  fun(i);
}

/* First instantiated from: insights.cpp:21 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void wrapper<int>(int && i)
{
  fun(i);
}
#endif


int main()
{
  wrapper(4);
  return 0;
}

所以 i 的右值类型应该是 int&&。问题是:为什么我在这里需要 std::forward 因为编译器已经知道 iint&& 而不是 int& 但仍然调用 fun(it&)?

why do I need std::forward here since compiler already knows that i is int&& not int& but still calls fun(it&)?

i 的类型是 int&&,但 i 本身是一个 lvalue。因此,当您调用 fun(i) 时,由于 i 本身是一个 左值 ,编译器将选择 fun(int &).

如果你想调用fun(int &&),你可以使用std::move将其转换为右值

fun(std::move(i));

why do I need std::forward here since compiler already knows that i is int&& not int& but still calls fun(it&)?

因为 i 当使用 as/in 表达式如调用 fun(i) 是一个 左值 。即当使用as/in表达式时i的值类别是lvalue。因此调用 fun(i) 选择第一个重载 (void fun(int&)).

另一方面,i 声明类型 int&&int.[=20 的右值引用=]

类型和 value categories 是不同的东西。

Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category.

i,变量名,是左值表达式,连变量的类型都是rvalue-reference.

The following expressions are lvalue expressions:

  • the name of a variable, ... Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
  • ...

这就是我们应该使用 std::forward to preserve the original value category of a forwarding reference 参数的原因。