为什么 std::forward 有两个重载?
Why does std::forward have two overloads?
给定以下引用折叠规则
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
总是使用显式模板参数调用,因此我们需要考虑两种情况:
forward<Type&>(val)
这里 forward
中 T
的类型将是 T&
因此 return 类型将是 [=13 的身份转换=]
forward<Type&&>(val)
这里 forward
中 T
的类型将是 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),最后一种情况是传递悬空左值引用。
给定以下引用折叠规则
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
总是使用显式模板参数调用,因此我们需要考虑两种情况:
forward<Type&>(val)
这里forward
中T
的类型将是T&
因此 return 类型将是 [=13 的身份转换=]forward<Type&&>(val)
这里forward
中T
的类型将是T&&
因此 return 类型将是 [=19 的身份转换=]
那么为什么我们需要 http://en.cppreference.com/w/cpp/utility/forward 中描述的两个重载?
注意:我不确定 std::forward
是否曾与 const
类型一起使用,但在那种情况下我禁用了 forward
,因为我从来没有见过它被这样使用过。在那种情况下,移动语义也没有真正意义。
Howard 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&& ) { }
我可以将非 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),最后一种情况是传递悬空左值引用。