为什么 std::forward 将左值和右值转换为右值引用?

Why does std::forward converts lvalue and rvalue to rvalue reference?

我想我对 std::forward 感到困惑。 下面是我使用 std::forward 的函数,但为了便于解释,它进行了很多简化和修改。

// This is an example code to explain my question simply.
template <typename Element>
void add(Element&& element) {
    static std::vector vec;
    vec.push_back(std::forward<Element>(element));
}

我用上面的功能尝试了两种情况; Case 1 左值参数和 Case 2 右值参数。

案例 1:左值参数

auto some_class = SomeClass();
add(some_class);

案例 2:右值参数

add(SomeClass());

在调试器中,两种情况都通过相同的以下部分,std::forward 部分和 std::vector 部分。

std::forward 部分:

template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }

std::vector 部分:

#if __cplusplus >= 201103L
  void
  push_back(value_type&& __x)
  { emplace_back(std::move(__x)); }

似乎 std::forward 部分将两种情况都转换为右值引用,&&,因为它使用 static_cast<_Tp&&>。并且 std::vector 将两个元素都视为右值引用,因为它使用 std::move().

我预计情况 1 的扩充是左值,因为它有自己的名称,而情况 2 是右值,因为它没有自己的名称。 我还期望 std::forward 将案例 1 转换为左值引用,将案例 2 转换为右值引用。 我对左值、右值和 std::forward 的理解正确吗?如果是这样,为什么 std::forward 将两者都转换为右值引用 &&.

如果我犯了错误,很抱歉占用您的时间。

您缺少的部分是引用折叠。当传入一个左值时,对于某些 T,它将具有 T&(或 const T&)类型。如果将其添加到 forward 模板中,您将得到:

return static_cast<T& &&>(__t);

由于引用折叠规则,这会折叠为 T&

Effective Modern C++ 在第 28 项中对此进行了介绍。基本上:

  • 类型 T 的左值推导为 T&
  • 类型 T 的右值推导为 T

有了这个,以及上面的参考折叠规则,希望你能理解 std::forward 是如何工作的。

why std::forward converts both as rvalue reference

不应该。根据forwarding reference的规则,当一个左值被传递给add时,模板类型参数Element将被推导为SomeClass&。然后 std::forward<SomeClass&>(element) 将被调用,并且 std::forward 的实例化将是

// before reference collapsing
constexpr SomeClass& &&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass& &&>(__t); }

// after reference collapsing
constexpr SomeClass&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass&>(__t); }

所以对于第一种情况,std::forward 将是 return 一个左值。来自函数的左值引用 return 是左值。

顺便说一句,对于第二种情况,templare参数Element将被推导为SomeClass,然后你可以像上面一样进行推理,最后实例化std::forward会是

constexpr SomeClass&&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass&&>(__t); }

从函数 return 编辑的右值引用是右值。


你得到的结果看起来很奇怪,对于第一种情况,应该调用 std::vector::push_back(const T&)。 (我试了一个mcve, here