我有以下代码:
#include <iostream>
template <typename T>
void f(T& x)
{
std::cout << "f(T& )" << std::endl;
}
template <typename T>
void f(const T& x)
{
std::cout << "f(const T& )" << std::endl;
}
int main()
{
int a = 0;
const float b = 1.1;
f(a); // call f(T&)
f(b); // call f(const T&)
}
输出结果为:
f(T& )
f(const T& )
我的问题是:编译器如何知道要调用哪个函数?如果我从函数定义中删除引用,那么我会得到一个“模糊调用”类型的错误,即error: redefinition of 'f'
。对我来说,看起来f(T&)
同样可以用于两个调用,为什么const
版本明确要求f(b)
?
答案 0 :(得分:9)
鉴于两个竞争的重载,标准要求编译器选择具有“最佳匹配”的重载。 (如果没有唯一的最佳过载,或者如果无法访问唯一的最佳过载,则程序格式不正确。)
在这种情况下,规则由§13.3.3.2[over.ics.rank] / p3提供:
标准转换序列S1是比标准转换序列S2更好的转换序列,如果:
[...]
S1和S2是引用绑定(8.5.3),引用引用的类型除了顶级cv限定符之外是相同的类型,并且S2引用的引用引用的类型是比由S1初始化的引用所引用的类型更符合cv。
这是标准中给出的例子:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
int k = g(i); // ambiguous
在您的情况下,const T&
比T&
更符合资格,因此根据标准,f(T&)
比f(const T&)
更合适并且通过重载决策选择
答案 1 :(得分:6)
f(T&)
与f(T const&)
这两个函数是不同的,因为第一个签名表明通过引用传递的任何变量都可以被函数修改。因此const float不能传递给第一个函数,第二个函数是编译器唯一可行的选择。 nonconst变量可以传递给两者,因此编译器必须选择更合适的变量(如果有的话)。标准说,为了调用第二个函数,编译器必须向任何nonconst变量添加const
,而对于第一个函数,这不是必需的。添加const是一种隐式转换,它更糟糕&#34; converison(读作 more 转换步骤)比不添加任何内容。因此,标准要求编译器在传递非对象变量时选择第一个函数
如果您想知道:文字和临时文件不能绑定到非对象引用,那么f(4)
,f("meow")
和f(someFunc())
都会调用第二个函数。
f(T)
与f(const T)
它们看起来不同,但在重载分辨率或功能签名方面却没有。它们都是按值调用,或者是编译器:将参数的副本传递给函数。唯一的区别在于函数定义,您需要变量在函数体中保持不变。任何函数声明都不会影响函数定义签名中的变量定义:
void f(int); //a declaration
void f(int i); //redeclaration of the same function
void f(int const); //still the same function redeclared
void f(int const i2); //yes... a redeclaration
void f(int const i) { //at last a function definition and the copy of the argument used in the function body is required to be const
//...
}
void f(int i) { //there is only one f, so this is a redefinition!
//...
}
这不是&#34; ambuguos调用类型错误&#34;,因为编译器只有一个函数而且没有歧义。错误只是你确实两次确定相同的功能。因此,在许多样式指南中,首选函数声明没有顶级const,编译器通常会忽略它们,而不会在错误或警告消息中提及它们。