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

时间:2016-07-13 06:41:49

标签: c++ c++11 reference rvalue perfect-forwarding

鉴于以下参考折叠规则

  • 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));
}

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

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

  1. forward<Type&>(val)此处Tforward的类型将为T&,因此返回类型将是T&的身份转换
  2. forward<Type&&>(val)此处Tforward的类型将为T&&,因此返回类型将是T&&的身份转换
  3. 那么为什么我们需要两个重载,如http://en.cppreference.com/w/cpp/utility/forward

    中所述

    注意:我不确定std::forward是否与const类型一起使用,但在这种情况下我禁用forward,因为我从未见过就这样使用了。在这种情况下,移动语义也不是很有意义。

1 个答案:

答案 0 :(得分:7)

一个好的起点是Howard Hinnant的answerpaper 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&& ) { }

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

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

和rvalue变种:

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

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

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

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