正如标题所说,为什么命名变量调用被解析为T&&
而不是const T&
函数?
#include <iostream>
template<typename T>
void f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
}
template<typename T>
void f(const T& v)
{
std::cout << "void f(const T& v)" << std::endl;
}
int main()
{
int i = 0;
f(1);
f(i);
}
在这种情况下,即使f()
已命名,两个调用都会解析为i
的第一个版本。一种解决方案是添加:
template<typename T>
void f(T& v)
{
std::cout << "void f(T& v)" << std::endl;
}
或将第一个版本更改为:
template<typename T>
typename std::enable_if<!std::is_reference<T>::value, void>::type f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
}
但我想了解这一决定背后的原因。
答案 0 :(得分:14)
扣除额为T = int &
,表示f(T &&) == f(int &)
。重载决策规则([over.ics.rank / 13.3.3.2])表示这是一个比f(int const &)
更严格的匹配。两者都被归类为“完全匹配”(将值绑定到引用),但首选CV参数较少的参考。
答案 1 :(得分:1)
一个较少受到标准限制的答案是在声明中承认:
template<typename T>
void f(T&& v)
因为T
是推导类型,T&& v
是Scott Meyers所谓的universal reference,它可以绑定到rvalues和lvalues。 T
可以推断为引用类型,因此您实际上正在调用f<int&>(int& && v)
,此时引用折叠规则生效,使此函数f<int&>(int& v)
的名义签名成为,正如前面的答案所指出的,它是非const int
参数的更好匹配。
在迈耶斯&#39;即将到来的Effective Modern C++他有以下项目:
第31项告诉你不该做什么。第30项建议你可以做什么,只写一个f
的重载,并用v
完全转发std::forward
:
template<typename T>
void f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
if (std::is_rvalue_reference<decltype(v)>::value)
std::cout << "rvalue" << std::endl;
else
std::cout << "lvalue" << std::endl;
std::cout << "v = " << std::forward<T>(v) << std::endl;
}