【发布时间】:2020-12-01 20:43:46
【问题描述】:
我已经了解std::move 的工作原理,并实现了我自己的版本,仅供练习。现在我想了解std::forward 的工作原理:
到目前为止我已经实现了这个:
#include <iostream>
template <typename T>
T&& forward_(T&& x)
{
return static_cast<T&&>(x);
}
/*template <typename T>
T&& forward_(T& x)
{
return static_cast<T&&>(x);
}*/
void incr(int& i)
{
++i;
}
void incr2(int x)
{
++x;
}
void incr3(int&& x)
{
++x;
}
template <typename T, typename F>
void call(T&& a, F func)
{
func(forward_<T>(a));
}
int main()
{
int i = 10;
std::cout << i << '\n';
call(i, incr);
std::cout << i << '\n';
call(i, incr2);
std::cout << i << '\n';
call(0, incr3); // Error: cannot bind rvalue reference of type int&& to lvalue of type int.
std::cout << "\ndone!\n";
}
为什么我必须提供采用左值引用的重载forward(T&) 版本?据我了解,转发引用可以根据其参数的类型产生左值或右值。因此,将右值文字0 传递给call 以及采用int&& 类型的右值引用的incr3 函数通常不需要forward<T>(T&)?!
-
如果我取消注释
forward_(T&)版本,它可以正常工作!? -
我仍然很困惑:为什么如果我只使用
forward_(T&)版本,它是否适用于任何价值类别?那么让那个转发引用forward_(T&&)有什么意义呢? -
如果我取消注释将左值引用引用到
T&的版本和转发引用T&&的版本,那么代码工作正常,我在两者中添加了一些消息以检查调用了哪个。结果是T&&从未调用过的那个!template <typename T> T&& forward_(T& x) { std::cout << "forward_(T&)\n"; return static_cast<T&&>(x); } template <typename T> T&& forward_(T&& x) { std::cout << "forward_(T&&)\n"; return static_cast<T&&>(x); } -
我的意思是在上面显示的驱动程序中运行相同的代码。
【问题讨论】:
-
您不需要重载版本...
forward(T&)是您应该拥有的唯一版本。 “我猜转发引用可以根据其参数的类型产生左值或右值。”不,引用的类型取决于它的模板参数而不是它的参数。 -
@BenVoigt:是的,如果我使用仅对
T进行左值引用的版本,它就可以工作,但是为什么标准添加了带有转发引用的版本?T&&? -
@Maestro 见stackoverflow.com/q/27501400/580083。 TL;DR:它强制您为
std::forward提供显式模板参数。现场演示:godbolt.org/z/ed8GKb