我正在尝试完美转发,发现
std::forward()
需要两个重载:
过载nr。 1:
template <typename T>
inline T&& forward(typename
std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
过载nr.2:
template <typename T>
inline T&& forward(typename
std::remove_reference<T>::type&& t) noexcept
{
static_assert(!std::is_lvalue_reference<T>::value,
"Can not forward an rvalue as an lvalue.");
return static_cast<T&&>(t);
}
现在,完美转发的典型场景是
template <typename T>
void wrapper(T&& e)
{
wrapped(forward<T>(e));
}
当然,您知道在实例化wrapper()
时,T
取决于传递给它的参数是左值还是右值。如果它是类型U
的左值,则将T
推导为U&
。如果是右值,则将T
推导为U
。
在任何情况下-在wrapper()
的范围内-e
都是左值,因此它总是使用std::forward()
的第一个重载。
现在我的问题:
使用(并且需要)第二次重载的有效方案是什么?
答案 0 :(得分:13)
N2951中详细讨论了forward
的设计原理。
该文档列出了6个用例:
A。 应将左值作为左值转发。所有实现均通过 这个测试。但这不是经典的完美转发模式。的 该测试的目的是为了表明实现2的失败 提出了防止除完美转发之外的所有用例的目标。
B。 应将一个右值作为右值转发。与用例A一样,这是 身份转换,这是一个激励人的例子 需要身份转换的地方。
C。 应该不将右值作为左值转发。 表现出意外产生悬挂的危险情况 参考。
D。 应将较少的简历限定表达式转发给更多 符合简历要求的表达式。 在前进过程中添加const。
E。 应将派生类型的表达式转发给可访问的, 明确的基本类型。 派生类型转换为基本类型。
F。 应该不转发任意类型的转换。这种情况 演示在远期潜在客户中如何进行任意转化 悬而未决的参考运行时错误。
第二次重载启用情况B和C。
本文继续提供了每个用例的示例,这些示例太冗长,无法在此处重复。
更新
我刚刚通过这6个用例运行了第一个重载的“解决方案”,此练习表明,第二个重载也启用了用例F:不应转发任意类型转换。
答案 1 :(得分:1)
这是第二次重载的一种非常普遍的用法:通用lambda。
[](auto&& x) {
return f(std::forward<decltype(x)>(x));
}
没有第二次重载,代码将使用std::conditional_t<std::is_rvalue_reference_v<decltype(x)>, ...>
。
(请参阅Scott Meyer的C++14 Lambdas and Perfect Forwarding)