我正在阅读Overview of the New C++ (C++11/14) (PDF only),在幻灯片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); }
有什么区别?为什么后者通常是实施?
答案 0 :(得分:20)
第一个问题是你可以写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;
答案 1 :(得分: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);
}
但请注意,在C ++ 14中,std::forward
为constexpr
。
答案 2 :(得分:3)
Sebastian Redl所说的第一个案例总会给你一个左值参考。原因是参数中的右值引用将作为左值引用传递,参数android.intent.action.BOOT_COMPLETED
类型为universal reference而不是右值引用。
实际上,如果第一种情况是正确的,我们甚至不再需要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;
}
和t
从u
传递到f
都是左值引用,尽管g
是u
中的右值引用{1}}。因此,在第一种情况下,f
的参数只是没有机会成为右值参考。
forward
用于将参数类型从通用引用更改为右值引用(如Redl所述,使用identity
更精确)。但是,此更改不再允许模板类型推导,因此std::remove_reference
的类型参数是必需的,因此我们将编写forward
。
但你问题中的第二个案例也不正确,正如Redl所提到的,正确的方法是重载,其参数是左值参考。
我能找到的最直接的实现是
forward<T>(t)
它适用于通用引用,例如
template <typename T>
T&& forward(typename identity<T>::type& param)
{
return static_cast<T&&>(param);
}
答案 3 :(得分:0)
有什么区别?
在实践中没有区别,执行的预期结果是一样的,只是写的方式更冗长
您只是定义 T 并使用通过 typename 定义的类型,这意味着实际上没有区别。
<块引用>为什么后者是通常的实现方式?
我没有关注这个更新,所以我不能说他们是否在他们的存储库中正式使用了这段代码,这可能是别人的实现而不是官方的,不管是什么原因:
他们选择了这种方式,尽管两种实现对于实际场景都是无效的。
注意两者的结果是一样的,在执行过程中根本没有分歧,也就是说,在实际场景中,它们之间的分歧不会导致任何差异或运行/编译错误或类似的事情
错过添加“typename”以使编译成功
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{
return static_cast<**typename** identity<T>::type&&>(param);
}
#include <iostream>
template<typename T>
T&& forward(T&& param)
{
return static_cast<T&&>(param);
}
int main() {
int value = 5;
forward<int>(value);
return 1;
}
该错误是因为引用 void redir 的 模板 T 属于 int 类型,并且在调用 forward< int > 时(param) 它正在传递 param 这是一个左值变量,在 Why is invalid
部分中进行了解释#include <iostream>
template<typename T>
T&& forward(T&& param)
{
return static_cast<T&&>(param);
}
void print(int &&value){
std::cout << "rvalue: " << value << std::endl;
}
void print(int &value){
std::cout << "lvalue: " << value << std::endl;
}
template <class T>
void redir(T &¶m){
print(forward<T>(param));
}
int main() {
int value = 5;
redir(value);
**redir(5);**
return 0;
}
代码类似于:
#include <iostream>
template< class T >
T&& forward( std::remove_reference_t<T> &¶m)
{
return static_cast<T&&>(param);
}
template< class T >
T&& forward( std::remove_reference_t<T> ¶m)
{
return static_cast<T&&>(param);
}
void print(int &&value){
std::cout << "rvalue: " << value << std::endl;
}
void print(int &value){
std::cout << "lvalue: " << value << std::endl;
}
template <class T>
void redir(T &¶m){
print(forward<T>(param));
}
int main() {
int value = 5;
redir(value);
redir(5);
return 0;
}
std::remove_reference_t 是可选的,即使不使用它,结果也是一样的。
由于良好的实践,他们决定使用 remove_reference_t,也就是说,他们强调第一个函数需要 int&,第二个函数需要 int&&。
好吧,但是结果解释没有改变是由于以下转换规则:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
正如我们所知,获得右值 (T&&) 的唯一方法是通过转换 T&& + T&& 或仅 T&&。
<int&&> int&& && forward(int&& &¶m) will result int&& forward(int&& param)
<int&> int& && forward(int& &¶m) will result int& forward(int& param)
<int> int && forward(int &¶m) will result int&& forward(int&& param)
注意第二个函数:T&& forward(std::remove_reference_t ¶m) 填补了缺失的部分
<int> int && forward(int ¶m) will result int&& forward(int& param)
因此,您需要声明 2 个 std::forward 函数。