折叠转发引用

Collapsing of forwarding references

给定代码

struct S{};

template <typename T>
auto foo(T&& t)
{
    static_assert(std::is_same_v<T, std::remove_cvref_t<T>>);
}

void test()
{
    const S s;
    foo(s);  // error

    foo(S{});

    S s2;
    foo(s2); // error
}

T 将推导为

  1. S const&
  2. S
  3. S&

为什么T不推导为

  1. S const
  2. S
  3. S

为什么 T 在左值情况下保留引用而不在右值情况下?

playground

转发引用的一个怪癖是你永远不会“只有一个值”。

T推导为S时,这只是因为函数实际上接受S&&

当您传递局部变量时,类型推导会识别出这些是左值,因此它将它们绑定到常规引用。在 const S s 的情况下 const 一个。函数签名“改变”以反映这一点。

您将拥有 (const S&)&&,标准指示折叠为仅 const S&,然后您将拥有 (S&)&&,其折叠为仅 S&,每个引用折叠的规则。

参考[dcl.ref]

See for yourself(现场演示)

就是这样 forwarding reference works in template argument deduction:

(强调我的)

4) If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward

这意味着,如果传递了一个左值,T 将被推导为左值引用。否则,如果T在转发引用中被推导为左值和右值的相同类型,则无法使用std::forward再次执行完美转发;即当传递左值时作为左值转发,当传递右值时作为右值转发。