我正在阅读新C++概述(C++ 11/14)(仅限PDF),在幻灯片288中它给出了一个实现std::forward
:
template<typename T> // For lvalues (T is T&),
T&& std::forward(T&& param) // take/return lvalue refs.
{ // For rvalues (T is T),
return static_cast<T&&>(param); // take/return rvalue refs.
}
Run Code Online (Sandbox Code Playgroud)
然后在文本中给出另一个实现:
通常的std :: forward实现是:
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{ return static_cast<identity<T>::type&&>(param); }
Run Code Online (Sandbox Code Playgroud)
有什么不同?为什么后者通常是实施?
Seb*_*edl 23
第一个问题是你可以编写std::forward(x)
,它不能做你想要的,因为它总是产生左值引用.
第二种情况中的参数是非推导的上下文,阻止自动推导模板参数.这迫使你写std::forward<T>(x)
,这是正确的事情.
此外,第二个重载的参数类型应该是typename identity<T>::type&
因为习惯用法的输入std::forward
始终是左值.
编辑:标准实际上要求签名等同于这一个(顺便提一下,这正是libc ++所具有的):
template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
Run Code Online (Sandbox Code Playgroud)
Wal*_*ter 11
libc ++中的实现使用了std::remove_reference
两个重载.这是源(删除一些宏后):
template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
template <class 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);
}
Run Code Online (Sandbox Code Playgroud)
但请注意,在C++ 14中,std::forward
是constexpr
.
neu*_*ont 11
Sebastian Redl 所说的第一种情况总是会给你一个左值引用。原因是参数中的右值引用会作为左值引用传递,并且参数T&&
类型是通用引用而不是右值引用。
实际上如果第一种情况是正确的,我们甚至不需要forward
更多。下面是一个实验来演示通用引用参数是如何传递的
template <typename T, typename U>
void g(T&& t, U&& u)
{
std::cout << "t is lvalue ref: "
<< std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
std::cout << "t is rvalue ref: "
<< std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
std::cout << "u is lvalue ref: "
<< std::is_lvalue_reference<decltype(u)>::value << std::endl; // 1
std::cout << "u is rvalue ref: "
<< std::is_rvalue_reference<decltype(u)>::value << std::endl; // 0
}
template <typename T, typename U>
void f(T&& t, U&& u)
{
std::cout << "t is lvalue ref: "
<< std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
std::cout << "t is rvalue ref: "
<< std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
std::cout << "u is lvalue ref: "
<< std::is_lvalue_reference<decltype(u)>::value << std::endl; // 0
std::cout << "u is rvalue ref: "
<< std::is_rvalue_reference<decltype(u)>::value << std::endl; // 1
g(t, u);
}
int main()
{
std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该计划原来,这两个t
并u
传递从f
到g
是左值的引用,尽管u
是在一个右值引用f
。所以在第一种情况下,forward
just的参数没有机会成为右值引用。
所述identity
用于从通用参考参数类型更改为一个右值参考(如通过雷德尔提到的,它更精确至使用std::remove_reference
)。然而,这种变化使得模板类型推导不再可能,因此类型参数 forforward
是强制性的,因此我们将编写forward<T>(t)
.
但是您问题中的第二种情况也不正确,正如 Redl 也提到的,正确的方法是重载,其参数是左值引用。
我能找到的最直接的实现是这个
template <typename T>
T&& forward(typename identity<T>::type& param)
{
return static_cast<T&&>(param);
}
Run Code Online (Sandbox Code Playgroud)
它适用于通用引用,例如
template <typename T, typename U>
void f(T&& t, U&& u)
{
::forward<T>(t);
::forward<U>(u);
}
std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
// deduction in f:
// T = unique_ptr&, decltype(t) = unique_ptr&
// U = unique_ptr, decltype(u) = unique_ptr&& (but treated as an lvalue reference)
// specialization of forward:
// forward<T> = forward<unique_ptr&>, param type = unique_ptr&
// return type = unique_ptr&
// forward<U> = forward<unique_ptr>, param type = unique_ptr&
// return type = unique_ptr&&
Run Code Online (Sandbox Code Playgroud)