我想我对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
部分将两个案例转换为右值引用&&
,因为它使用static_cast<_Tp&&>
。并且std::vector
将这两个元素视为右值引用,因为它使用std::move()
。
我预计案例1的增加是左值,因为它有自己的名称,案例2是右值,因为它没有自己的名字。
我也期望std::forward
将案例1转换为左值引用,将案例2转换为右值引用。
我对左值,右值和std::forward
的理解是否正确?如果是这样,为什么std::forward
会将两者都转换为右值引用&&
。
如果我犯了错误,我很抱歉花时间。
答案 0 :(得分:2)
为什么
std::forward
将两者都转换为右值参考
不应该。根据{{3}}的规则,当左值传递给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
将返回左值。从函数返回的左值引用是左值。
BTW,对于第二种情况,templare参数Element
将推导为SomeClass
,然后您可以进行与上述相同的推断,最后std::forward
的实例化将是
constexpr SomeClass&&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass&&>(__t); }
从函数返回的rvalue-reference是一个rvalue。
你得到的结果似乎很奇怪,对于第一种情况,应该调用std::vector::push_back(const T&)
。 (我尝试了forwarding reference,mcve)
答案 1 :(得分:1)
您缺少的部分是参考折叠。在传入左值时,对于某些T&
,它将具有const T&
(或T
)类型。如果您将其添加到forward
模板中,则会获得:
return static_cast<T& &&>(__t);
由于参考折叠规则,这会折叠为T&
。
有效的现代C ++在第28项中涵盖了这一点。基本上:
T&
。T
。有了这个,以及上面的参考折叠规则,希望你能理解std::forward
的工作原理。