我想知道这怎么可能?
template<typename T>
void Test(T&& arg)
{
arg = 14;
}
int a = 23;
Test(a);
我的问题是函数 Test 需要一个右值类型的参数,但是它似乎也接受左值类型的参数。这是为什么 ?那是因为模板的存在吗?因为如果我做这样的事情
void AnotherTest(int&& arg)
{
arg = 14;
}
然后函数要求参数是右值类型。 如果有人能解释为什么模板的存在会改变行为,我将不胜感激。
正如您正确想象的那样,关键是它是一个模板和参数类型被推导。当您使用左值调用
Test
时,参数类型推导规则 当参数是右值引用 时,会将类型 T
推断为 lvalue-reference,因此特化变为:
template <>
void Test<int&>(int & && arg)
此时引用折叠规则开始生效,参数类型变为:
template <>
void Test<int&>(int & arg)
虽然模板采用 rvalue-reference,如果类型是 lvalue-reference 参数本身就变成了 lvalue-reference。
那是因为模板的存在吗?
有点,但严格来说不是。更准确地说,出现此行为是因为您有一个引用类型的别名。模板参数是您最有可能看到的场景,但您可以在没有模板的情况下合成行为。
using T = int&; // Alias, not a template parameter
void AnotherTest(T&& arg)
{
arg = 14;
}
我没有使用模板参数作为别名,而是使用了
using
关键字(typedef
也可以)。就像在您的模板设置中一样,对左值引用的右值引用折叠到左值引用。也就是说,T&&
在这种情况下与 T
相同。所以你可以调用AnotherTest(a)
并让arg
绑定到a
.
另一方面,您可以保留模板并仍然复制错误。关键是要指定模板参数,而不是让它被推导。
Test<int>(a);
error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
上面的要点是你可能误解了编译器将你的模板参数推断为什么。当您调用
Test(a)
时,您可能认为 T
是 int
,但是您的编译器(对此事有最终决定权)决定 T
是 int&
。