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

Cur*_*ous 13 c++ reference rvalue perfect-forwarding c++11

鉴于以下参考折叠规则

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

第三和第四条规则意味着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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

  1. forward<Type&>(val)这里Tin 的类型forward将是T&,因此返回类型将是身份转换T&
  2. forward<Type&&>(val)这里Tin 的类型forward将是T&&,因此返回类型将是身份转换T&&

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

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

Bar*_*rry 7

一个良好的开端将是霍华德Hinnant(欣南特)的答案论文std::forward().


你的实现处理所有正常正确的用例(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&& ) { }
Run Code Online (Sandbox Code Playgroud)

我可以将非const引用传递给const对象,这两个都是左值的:

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

和右值品种:

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

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

foo(my_forward<Object&>(Object{}));   // ok?? calls foo<Object&>
foo(std::forward<Object&>(Object{})); // error
Run Code Online (Sandbox Code Playgroud)

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