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

4

我对 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));
}

我尝试使用上述函数进行了两种情况的测试;第一种情况是lvalue参数,第二种情况是rvalue参数。
情况1:lvalue参数
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()
我原本预期Case 1的增量是左值,因为它有自己的名称,而Case 2是右值,因为它没有自己的名称。 我也预期std::forward会将Case 1转换为左值引用,Case 2转换为右值引用。 我的对左值、右值和std::forward的理解正确吗?如果是这样,为什么std::forward将两者都转换为右值引用,&&
如果我犯了错误,很抱歉浪费了您的时间。
2个回答

3

为什么std::forward会将两者都转换为右值引用?

它不应该这样。根据转发引用的规则,当一个左值被传递给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 将返回一个左值。从函数返回的左值引用是一个左值。

顺便说一句,对于第二种情况,模板参数 Element 将被推断为 SomeClass,然后您可以进行与上述相同的推断,最后实例化 std::forward 将是:

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

从函数返回的右值引用是一个右值。


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


感谢您详细回答。我同意我上面的问题很奇怪。我再次阅读了我的代码。再次非常感谢您。 - mora

1
你所缺少的部分是引用折叠(reference collapsing)。当传递一个左值时,它将具有类型T&(或const T&),其中T是某个类型。如果你将这个内容添加到forward模板中,你将得到以下结果:
return static_cast<T& &&>(__t);

由于引用折叠规则,这将折叠为T&
《Effective Modern C++》中的第28项有所涉及。基本上:
  • 类型为T的Lvalue被推导为T&
  • 类型为T的Rvalue被推导为T
  • 有了这个,再加上上面的引用折叠规则,希望您能理解std::forward的工作原理。

    谢谢你告诉我参考资料。我会再读一遍。 - mora

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接