将 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::forwardstd::forward<U> 所做的只是将其参数转换为 U&&。如果 Usome_type,就像上面第二种情况一样,参数被转换为 some_type&&。它仍然是一个右值引用。如果Usome_type&,和上面第一种情况一样,再次进行引用折叠,some_type& &&变成some_type&。所以 std::forward return 是一个左值引用。

所以你最初问题的最终答案是 std::forward 的 return 类型仅取决于作为 std::forward 的模板参数传递的类型。由于 T 在您的情况下将始终被推断为非引用类型,因此 std::forward 将始终 return 右值引用。