上一篇简单演示了一下模板函数的打印输出功能
但是,如果同时存在
// 1
template <typename T, typename ... Args>
void printX(T t, Args...args);
// 2
template <typename ... Args>
void printX(T t, Args...args);
这时会出现什么情况呢?还能正常输出结果吗?
我们看下面的例子
#include <iostream>
#include <functional>
struct Customer
{
int val;
double db;
std::string str;
};
class CustomerHash
{
public:
size_t operator()(const Customer &c)
{
return hash_val(c.val, c.db, c.str);
}
template <typename... Types>
inline size_t hash_val(const Types&...args)
{
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
template <typename T, typename... Types>
inline void hash_val(size_t &seed, const T& val,
const Types&...args)
{
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename T>
inline void hash_val(size_t &seed, const T & val)
{
hash_combine(seed, val);
}
template <typename T>
inline void hash_combine(size_t &seed, const T& val)
{
seed ^= std::hash<T>()(val) + 0x9e3779b9
+ (seed << 6) + (seed >> 2);
}
};
int main()
{
Customer customer = {1, 23.45, "abc"};
size_t hash = CustomerHash()(customer);
return 0;
}
通过下断点,跟踪调试,可以发现, hash_val 函数调用顺序如下:
第一次进入最泛化的 hash_val 函数
template <typename... Types>
inline size_t hash_val(const Types&...args)
{
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
在函数内部又调用了hash_val函数
hash_val(seed, args...);
此时传入的第一个参数 seed类型为size_t,与该函数最接近
template <typename T, typename... Types>
inline void hash_val(size_t &seed, const T& val,
const Types&...args)
{
hash_combine(seed, val);
hash_val(seed, args...);
}
如果**args…**参数大于1的时候,会recursive递归调用自己
当参数个数等于1时,调用
template <typename T>
inline void hash_val(size_t &seed, const T & val)
{
hash_combine(seed, val);
}
跳出前面的递归调用。
测试这里,我们可以初步得出结论,当有多个模板函数匹配时,优先匹配最接近的一个调用。
但是,我们需要做进一步验证,比如把 struct Customer结构中第一个类型由int 改为 size_t
struct Customer
{
size_t val;
double db;
std::string str;
};
这样,在调用
size_t operator()(const Customer &c)
{
return hash_val(c.val, c.db, c.str);
}
的时候, c.val就是size_t类型,按照前面得出的结论,没改之前调用
template <typename... Types>
inline size_t hash_val(const Types&...args)
{
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
此时我们猜想应该会调用
template <typename T, typename... Types>
inline void hash_val(size_t &seed, const T& val, const Types&...args)
{
hash_combine(seed, val);
hash_val(seed, args...);
}
但事实上,我们通过下断点观测,调用顺序并没有发生改变,难道我们前面得出的结论错了吗???
这里我们还需要做一点小小的改动
size_t operator()(const Customer &c)
{
auto s = c.val;
return hash_val(s, c.db, c.str);
}
用一个变量先保存c.val的值,然后再传入参数,进行调用
这时我们编译就会报错
error C2440:“return”:无法从“void”转换为“size_t”
也就是说,现在调用到了
template <typename T, typename... Types>
inline void hash_val(size_t &seed, const T& val, const Types&...args)
{
hash_combine(seed, val);
hash_val(seed, args...);
}
由此看来,我们前面得出的结论需要进行修正:
当有多个模板函数匹配时,优先匹配最接近的一个调用,但类的成员变量,应该看作泛型T,而不是具体的int,double,或者size_t类型,应格外注意!!!