有时候模板需要将实参连同类型不变地转发给其它函数,在此情况下我们需要保持被转发实参的所有性质,包括是否是const以及是左值还是右值。
下面的代码中通过testFunc调用modifyValue并未改变value的值,原因是传递给testFunc的value是一个非引用类型。
template <typename F,typename T>
void testFunc(F f,T value)
{
f(value);
}
void modifyValue(int& value)
{
value = 100;
}
int main()
{
int value = 10;
testFunc(modifyValue, value);
cout << value << endl;
system("pause");
}
如果一个函数参数是指向模板类型参数的右值引用(T&&),它对应的实参的const属性和左值/右值属性将得到保持,修改后的testFunc成功改变了value的值。
template <typename F,typename T>
void testFunc(F f,T&& value)
{
f(value);
}
如果f的参数是一个右值引用是,又不行了,原因是即使传递一个右值给testFunc,由于传递给printValue的将是testFunc的参数,而函数的参数与其它任何变量一样都是左值,所以testFunc对printValue的右值引用参数会传递一个左值,导致编译器报错。
template <typename F,typename T>
void testFunc(F f,T&& value)
{
f(value);
}
void printValue(int&& value)
{
cout << value << endl;
}
int main()
{
int value = 10;
testFunc(printValue, value);
system("pause");
}
我们可以使用一个名为forward的标准库函数来传递参数,它能保持实参的类型,和move不同,forward必须通过显式模板实参来调用,forward返回该显式实参类型的右值引用,既std::forward<T>的返回类型是T&&。
当testFunc的实参value是一个右值时,T为一个普通类型,std::forward<T>返回T&&;当testFunc的实参value是一个左值时,T为一个左值引用类型,std::forward<T>返回类型是一个指向左值引用类型的右值引用,经过引用折叠,将返回一个左值引用类型。
template <typename F,typename T>
void testFunc(F f,T&& value)
{
f(std::forward<T>(value));
}
void printValue(int&& value)
{
cout << value << endl;
}
void modifyValue(int& value)
{
value = 100;
}
int main()
{
int value = 10;
testFunc(modifyValue, value);
testFunc(printValue, 123456);
system("pause");
}
forward和move都是类型转换函数,区别是不管一个左值还是右值,move都将返回一个右值引用,而forward会进行引用折叠。
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
return static_cast<_Ty&&>(_Arg);
}
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}