为什么我的 forward_ 函数对右值不起作用?
Why doesn't my forward_ function work for rvalues?
我已经了解 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
与采用类型 int&&
的右值引用的 incr3
函数一起传递给 call
通常不需要 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);
}
我的意思是 运行 我上面显示的驱动程序中的相同代码。
如果您手动指定 T
(而不是让编译器推断它),T&&
引用将不再是转发引用。如果 T
不是左值引用,则 T&&
是右值引用并且不接受左值。
例如,如果你做forward_<int>(...)
,那么参数是一个右值引用,...
只能是一个右值。
但是如果你这样做forward_(...)
,那么这个参数是一个转发引用并且...
可以有任何值类别。 (虽然这样称呼它是没有意义的,因为 forward_(x)
将具有与 x
本身相同的值类别。)
很明显你在想为什么有两个版本的std::forward
;一个采用对类型参数 T&
的左值引用,另一个采用对类型参数的通用引用(转发)。 T&&
.
在您的情况下,您正在使用函数模板 call
内部的 forward_
,该模板也具有转发引用。问题在于,即使 call
函数使用右值调用,它也始终使用 forward_
作为左值,因为 call
无法在没有对象(参数)的情况下传递其参数。请记住,对象的名称是一个 lvlaue,即使它是从右值初始化的。这就是为什么在您的示例中总是调用 forward_(T&)
的原因。
- 现在你问为什么有二版转发引用?
它非常简单,您可能已经猜到了:它用于 r 值(值而不是那些对象的名称)。
这是一个例子:
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);
}
int main()
{
int i = 10;
forward_(i); // forward(T&) (1)
forward_(5); // forward(T&&) (2)
forward_("Hi"); // forward(T&) (3)
}
我已经了解 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
与采用类型 int&&
的右值引用的 incr3
函数一起传递给 call
通常不需要 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); }
我的意思是 运行 我上面显示的驱动程序中的相同代码。
如果您手动指定 T
(而不是让编译器推断它),T&&
引用将不再是转发引用。如果 T
不是左值引用,则 T&&
是右值引用并且不接受左值。
例如,如果你做forward_<int>(...)
,那么参数是一个右值引用,...
只能是一个右值。
但是如果你这样做forward_(...)
,那么这个参数是一个转发引用并且...
可以有任何值类别。 (虽然这样称呼它是没有意义的,因为 forward_(x)
将具有与 x
本身相同的值类别。)
很明显你在想为什么有两个版本的std::forward
;一个采用对类型参数 T&
的左值引用,另一个采用对类型参数的通用引用(转发)。 T&&
.
在您的情况下,您正在使用函数模板 call
内部的 forward_
,该模板也具有转发引用。问题在于,即使 call
函数使用右值调用,它也始终使用 forward_
作为左值,因为 call
无法在没有对象(参数)的情况下传递其参数。请记住,对象的名称是一个 lvlaue,即使它是从右值初始化的。这就是为什么在您的示例中总是调用 forward_(T&)
的原因。
- 现在你问为什么有二版转发引用? 它非常简单,您可能已经猜到了:它用于 r 值(值而不是那些对象的名称)。
这是一个例子:
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);
}
int main()
{
int i = 10;
forward_(i); // forward(T&) (1)
forward_(5); // forward(T&&) (2)
forward_("Hi"); // forward(T&) (3)
}