【问题标题】:Why does std::forward converts lvalue and rvalue to rvalue reference?为什么 std::forward 将左值和右值转换为右值引用?
【发布时间】:2017-05-14 08:25:28
【问题描述】:

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

我用上面的函数尝试了两种情况;案例 1 左值参数和案例 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 部分将两种情况都转换为右值引用&amp;&amp;,因为它使用static_cast&lt;_Tp&amp;&amp;&gt;。并且std::vector 将两个元素都视为右值引用,因为它使用了std::move()

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

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

【问题讨论】:

    标签: c++ c++11 lvalue rvalue


    【解决方案1】:

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

    不应该。根据forwarding reference的规则,当一个左值被传递给add时,模板类型参数Element会被推导出为SomeClass&amp;。然后std::forward&lt;SomeClass&amp;&gt;(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 将返回一个左值。从函数返回的左值引用是一个左值。

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

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

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


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

    【讨论】:

    • 感谢您的详细回答。我同意我上面的问题很奇怪。我再次阅读了我的代码。再次非常感谢您。
    【解决方案2】:

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

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

    由于引用折叠规则,这将折叠到T&amp;

    Effective Modern C++ 在第 28 条中涵盖了这一点。基本上:

    • 类型 T 的左值被推导出为T&amp;
    • 类型 T 的右值被推导为T

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

    【讨论】:

    • 谢谢你告诉我参考。我会再读一遍。
    猜你喜欢
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多