为什么 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)
我想我对 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)