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

给定以下参考折叠规则

  • T&& --> T&
  • T&&& --> T&
  • T&&& --> T&
  • T&&&& --> T&&
  • T& & --> T&
  • T&& & --> T&
  • T& && --> T&
  • T&& && --> T&&

第三条和第四条规则意味着T(ref qualifer)&&是恒等变换,即T&停留在T&code> 和 T&& 保持在 T&&.为什么 std::forward 有两个重载?以下定义不能用于所有目的吗?

The third and fourth rule imply that T(ref qualifer) && is the identity transformation, i.e. T& stays at T& and T&& stays at T&&. Why do we have two overloads for std::forward? Couldn't the following definition serve all purposes?

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& 服务的唯一目的是不进行复制.并且 enable_if 有助于确保仅在非 const 值上调用该函数.我不完全确定是否需要 const_cast 因为它不是引用本身就是 const.

Here the only purpose the const std::remove_reference<T>& serves is to not make copies. And the enable_if helps ensure that the function is only called on non const values. I'm not entirely sure whether the const_cast is needed since it's not the reference itself that's const.

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

Since forward is always called with explicit template parameters there are two cases we need to consider:

  1. forward(val) 这里forwardT的类型就是T& 因此返回类型将是到 T&
  2. 的身份转换
  3. forward 这里forwardT的类型就是T&& 因此返回类型将是到 T&&
  4. 的身份转换
  1. forward<Type&>(val) Here the type of T in forward will be T& and therefore the return type will be the identity transformation to T&
  2. forward<Type&&>(val) Here the type of T in forward will be T&& and therefore the return type will be the identity transformation to T&&

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

So then why do we need two overloads as described in http://en.cppreference.com/w/cpp/utility/forward?

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

Note: I am not sure if std::forward is ever used with const types, but I disabled forward in that case, because I have never seen it used like that. Also move semantics don't really make sense in that case either.

推荐答案

Howard Hinnant 的 answer 是一个很好的起点> 和 paper 关于 std::forward().

A good place to start would be Howard Hinnant's answer and paper on std::forward().

您的实现正确处理所有正常用例(T& --> T&T const& --> T const&T&& --> T&&).它无法处理的是常见且容易产生的错误,这些错误在您的实现中很难调试,但无法使用 std::forward() 进行编译.

Your implementation handles all the normal use-cases correctly (T& --> T&, T const& --> T const&, and T&& --> T&&). What it fails to handle are common and easy-to-make errors, errors which would be very difficult to debug in your implementation but fail to compile with 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 对象,包括左值类型:

I can pass non-const references to const objects, both of the lvalue variety:

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

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

I can pass lvalue references to rvalues:

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

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

The first two cases lead to potentially modifying objects that were intended to be const (which could be UB if they were constructed const), the last case is passing a dangling lvalue reference.

相关文章