函数重载
三个阶段: 名称查找, 模板函数处理, 重载决议
前两个阶段得到候选集, 最后一个阶段选出最合适的版本
例
namespace animal
{
struct Cat{};
void feed(Cat *foo, int);
}
struct CatLike
{
CatLike(animal::Cat *);
};
template<typename T>
void feed(T *obj, double);
template<>
void feed(animal::Cat *obj, double d);
以下会调用哪一个版本?
animal::Cat cat; feed(&cat, 1);
以下分析
名称查找
成员函数名称查找: 通过. ->调用时, 在对应的成员类里查找
限定名称查找: 通过::调用时进行的查找
未限定名称查找: 可以通过参数依赖查找规则查找
注意参数依赖查找规则(ADL): 除了通常的作用域外, 实参类型对应的命名空间也在查找范围内
如下, 由于参数v的类型vector<int>在std命名空间里, 所以只调用sort却成功查找到了std::sort
#include "vector" int main() { std::vector<int> v; sort(v.begin(),v.end()); }
考虑另一种情形std::cout<<" " , 对std::cout调用的operator<<也没有注明命名空间,
在操作符重载等场景中, 此规则大大简化了调用
ADL规则仅在未限定名称查找时有效(函数名左边不能有 . -> ::), 可以手动为函数添加括号禁用ADL规则
#include "vector" int main() { std::vector<int> v; (sort)(v.begin(),v.end()); //错误, sort未定义 }
上面中sort未定义
经过查找后剩下的候选函数
void animal::feed(Cat *foo, int); void feed(CatLike); template<typename T> void feed(T *obj, double);
模板函数处理
对以下进行实例化
template<typename T> void feed(T *obj, double);
得到
template<animal::Cat> void feed(animal::Cat *obj, double);
不过, 若模板参数推导与替换失败, 则会删除该候选函数
如
template<typename T> void feed(T *obj, typename T::value_type);
实例化出
template<animal::Cat> void feed(animal::Cat *obj, animal::Cat::value_type);
其中Cat并没有value_type这个成员变量, 因此替换失败, 删除该候选集, 但此时还没有错误(SFINAE机制), 当最后候选集为空时才发生编译错误
重载决议
剩下的候选集
void animal::feed(Cat *foo, int);
void feed(CatLike);
template<animal::Cat>
void feed(animal::Cat *obj, double);
由于第二个版本参数不匹配, 最后只剩下
void animal::feed(Cat *foo, int);
template<animal::Cat>
void feed(animal::Cat *obj, double);
最后根据规则决策最佳函数
1, 类型最匹配, 转换最少的
2, 非模板函数优于模板函数
3, 若多于两个模板实例, 最具体的实例最佳
根据规则1, 调用feed(&cat,1);将使用第一个版本
而调用feed(&cat,1.0);将使用第二个版本
若候选集为
void animal::feed(Cat *foo, int);
template<animal::Cat>
void feed(animal::Cat *obj, int);
仍是调用feed(&cat,1);, 根据规则2, 将使用第一个版本(非模板函数)
若候选模板函数为
template<typename T>
void feed(T *obj, double);
template<typename T&g