为什么 std::forward 有两个重载?

Why does std::forward have two overloads?

给定以下引用折叠规则

第三条和第四条规则暗示T(ref qualifer) &&是恒等变换,即T&停留在T&T&&停留在T&&。为什么 std::forward 有两个重载?以下定义不能满足所有目的吗?

template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& forward(const typename std::remove_reference<T>::type& val) {
    return static_cast<T&&>(const_cast<T&&>(val));
}

这里 const std::remove_reference<T>& 的唯一目的是不复制。 enable_if 有助于确保函数仅在非 const 值上调用。我不完全确定是否需要 const_cast,因为引用本身不是常量。

由于 forward 总是使用显式模板参数调用,因此我们需要考虑两种情况:

  1. forward<Type&>(val) 这里 forwardT 的类型将是 T& 因此 return 类型将是 [=13 的身份转换=]
  2. forward<Type&&>(val) 这里 forwardT 的类型将是 T&& 因此 return 类型将是 [=19 的身份转换=]

那么为什么我们需要 http://en.cppreference.com/w/cpp/utility/forward 中描述的两个重载?

注意:我不确定 std::forward 是否曾与 const 类型一起使用,但在那种情况下我禁用了 forward,因为我从来没有见过它被这样使用过。在那种情况下,移动语义也没有真正意义。

Howard Hinnant 在 std::forward() 上的 and paper 是一个不错的起点。


您的实现可以正确处理所有 正常 用例(T& --> T&T const& --> T const&T&& --> T&&)。它无法处理的是常见且容易犯的错误,这些错误在您的实现中很难调试,但无法通过 std::forward() 编译。

给出这些定义:

struct Object { };

template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& my_forward(const typename std::remove_reference<T>::type& val) {
    return static_cast<T&&>(const_cast<T&&>(val));
}

template <class T>
void foo(T&& ) { }

我可以将非 const 引用传递给 const 对象,两者都是左值类型:

const Object o{};
foo(my_forward<Object&>(o));    // ok?? calls foo<Object&>
foo(std::forward<Object&>(o));  // error

和右值品种:

const Object o{};
foo(my_forward<Object>(o));    // ok?? calls foo<Object>
foo(std::forward<Object>(o));  // error

我可以将左值引用传递给右值:

foo(my_forward<Object&>(Object{}));   // ok?? calls foo<Object&>
foo(std::forward<Object&>(Object{})); // error

前两种情况导致可能修改本应为 const 的对象(如果它们是 const 构造的,则可能是 UB),最后一种情况是传递悬空左值引用。