将 std::forward 与非转发的普通旧引用一起使用
Using std::forward with a non-forwarding, plain old reference
找朋友:
为什么以下代码中的 std::forward
将参数 c
转换为右值?
template <typename T>
void f (T& c) {
using value_type = typename std::remove_reference_t<T>;
std::vector<value_type> v;
// debugger reveals: push_back( T&& value ) is called here
v.push_back(std::forward<T>(c));
}
请注意,c
不是此处的 universal/forwarding 引用。我知道这个功能如果真的有用的话,很可能会有更多用处,但还是很好奇。
嗯,std::forward<T>(x)
的定义就是把x
转换成T&&
。如果你传递一个非引用作为参数,你会得到一个右值引用 T&&
返回。因为你的T
不能是引用类型(不能有对引用的引用),所以一定是非引用类型。
std::forward<T>(c)
等同于 static_cast<T&&>(c)
.
如果 T&&
是转发引用那么这允许将左值作为左值转发,因为 T
将被推断为左值引用类型并且 T&&
将相同引用折叠规则的左值引用类型。在您的情况下,T&&
不是转发引用,因此这不起作用。
要了解这种情况,您必须了解为什么转发引用有效。给定一个定义
template <typename T>
void foo(T&& t) {}
当你写类似
的东西时
some_type some_object;
foo(some_object);
模板推导将 T
推导为 some_type&
。现在参数 t
的类型为 some_type& &&
。由于您无法引用引用,因此应用 reference collapsing 规则并将 some_type& &&
折叠为 some_type&
.
如果相反,你写的是
some_type some_object;
foo(std::move(some_object));
模板推导将 T
推导为 some_type
。现在参数 t
的类型为 some_type&&
。这是一个完全有效的类型,所以没有引用折叠。
现在我们到了 std::forward
。 std::forward<U>
所做的只是将其参数转换为 U&&
。如果 U
是 some_type
,就像上面第二种情况一样,参数被转换为 some_type&&
。它仍然是一个右值引用。如果U
是some_type&
,和上面第一种情况一样,再次进行引用折叠,some_type& &&
变成some_type&
。所以 std::forward
return 是一个左值引用。
所以你最初问题的最终答案是 std::forward
的 return 类型仅取决于作为 std::forward
的模板参数传递的类型。由于 T
在您的情况下将始终被推断为非引用类型,因此 std::forward
将始终 return 右值引用。
找朋友:
为什么以下代码中的 std::forward
将参数 c
转换为右值?
template <typename T>
void f (T& c) {
using value_type = typename std::remove_reference_t<T>;
std::vector<value_type> v;
// debugger reveals: push_back( T&& value ) is called here
v.push_back(std::forward<T>(c));
}
请注意,c
不是此处的 universal/forwarding 引用。我知道这个功能如果真的有用的话,很可能会有更多用处,但还是很好奇。
嗯,std::forward<T>(x)
的定义就是把x
转换成T&&
。如果你传递一个非引用作为参数,你会得到一个右值引用 T&&
返回。因为你的T
不能是引用类型(不能有对引用的引用),所以一定是非引用类型。
std::forward<T>(c)
等同于 static_cast<T&&>(c)
.
如果 T&&
是转发引用那么这允许将左值作为左值转发,因为 T
将被推断为左值引用类型并且 T&&
将相同引用折叠规则的左值引用类型。在您的情况下,T&&
不是转发引用,因此这不起作用。
要了解这种情况,您必须了解为什么转发引用有效。给定一个定义
template <typename T>
void foo(T&& t) {}
当你写类似
的东西时some_type some_object;
foo(some_object);
模板推导将 T
推导为 some_type&
。现在参数 t
的类型为 some_type& &&
。由于您无法引用引用,因此应用 reference collapsing 规则并将 some_type& &&
折叠为 some_type&
.
如果相反,你写的是
some_type some_object;
foo(std::move(some_object));
模板推导将 T
推导为 some_type
。现在参数 t
的类型为 some_type&&
。这是一个完全有效的类型,所以没有引用折叠。
现在我们到了 std::forward
。 std::forward<U>
所做的只是将其参数转换为 U&&
。如果 U
是 some_type
,就像上面第二种情况一样,参数被转换为 some_type&&
。它仍然是一个右值引用。如果U
是some_type&
,和上面第一种情况一样,再次进行引用折叠,some_type& &&
变成some_type&
。所以 std::forward
return 是一个左值引用。
所以你最初问题的最终答案是 std::forward
的 return 类型仅取决于作为 std::forward
的模板参数传递的类型。由于 T
在您的情况下将始终被推断为非引用类型,因此 std::forward
将始终 return 右值引用。