我正在阅读新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.
}然后在文本中给出了另一种表示:
通常的
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);
}有什么关系?为什么后者是通常的实现?
发布于 2014-12-16 09:36:10
第一个问题是,您可以编写std::forward(x),因为它总是生成lvalue引用,所以它不会执行您想要的操作。
第二种情况下的参数是一个非推导的上下文,防止了模板参数的自动推导。这迫使您编写std::forward<T>(x),这是正确的做法。
另外,第二个重载的参数类型应该是typename identity<T>::type&,因为惯用std::forward的输入总是一个lvalue。
编辑:标准实际上要求一个与此签名等价的签名(顺便说一句,这正是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;发布于 2014-12-16 09:50:15
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);
}但请注意,在C++14中,std::forward是constexpr。
发布于 2016-07-11 02:04:47
第一个案例,正如Sebastian所说,总是会给你一个无价值的参考。原因是参数中的rvalue引用将作为lvalue引用传递,而参数T&&类型是通用参考而不是rvalue引用。
实际上,如果第一种情况是正确的,我们甚至不再需要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;
}程序证明,t和u从f传递到g都是lvalue引用,尽管u是f中的rvalue引用。因此,在第一种情况下,forward的参数没有机会成为rvalue引用。
identity用于将参数类型从通用引用更改为rvalue引用(正如Redl所提到的,使用std::remove_reference更精确)。但是,这种更改使得模板类型的推断不再可能,因此forward的类型参数是强制性的,因此我们将编写forward<T>(t)。
但是问题中的第二种情况也是不正确的,正如Redl所提到的,正确的方法是重载,其参数是lvalue引用。
我能找到的最直接的实现是
template <typename T>
T&& forward(typename identity<T>::type& param)
{
return static_cast<T&&>(param);
}例如,它适用于通用引用。
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&&https://stackoverflow.com/questions/27501400
复制相似问题