最近在学习侯捷老师的c++视频。其中关于variadic template的讲义25:00处的一处讲述似乎不对,特地拿出来与大家探讨。视频源
https://www.youtube.com/watch?v=TJIb9TGfDIw&list=WL&index=4&t=1414shttps://www.youtube.com/watch?v=TJIb9TGfDIw&list=WL&index=4&t=1414s在第25分钟处,侯老师说,当args...有零个参数才会调用第三个hash_val(原话是“只剩一个种子和0个参数”)。似乎不对。应为当args...有1个参数就会调用第三个hash_val。
讲一下我的理解:
1 既然仿函数CustomHash有返回值,所以它调用的hash_val就必须是有返回值的第三个函数。
2 第三个hash_val又会调用如下:
hash_val(seed, args...);
理论上说,这可以是自身的递归调用,但是由于seed是size_t类型,跟第一、第二个函数更匹配,所以应从前两个函数里选一个调用。
第一个函数的第二个参数只能是单个变量,而args...是一包(准确的说,是三个变量)。所以第二个函数更匹配,因此调用第二个函数hash_val
3 第二个函数又一次调用了
hash_val(seed, args...);
此时args...含有两个变量。所以第一个hash_val仍然不合适,还是要调用第二个hash_val
4 第二个函数再一次调用了
hash_val(seed, args...);
此时args...含有1个变量。所以第一个hash_val最合适,应该被调用。
最终的次序是,先调用第一个函数,再调用第二个,然后再调用第二个,最后调用第一个hash_val。
附上代码检验:
#include <functional>
#include <string>
#include <iostream>
template <typename T>
inline void hash_combine(size_t& seed, const T & val)
{
seed ^= std::hash<T>()(val);
}
template <typename T>
inline void hash_val(size_t & seed, const T & val)
{
int iIdx = 1;
printf("%d\n", iIdx);
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(size_t & seed, const T & val, const Types&... args)
{
int iIdx = 2;
printf("%d\n", iIdx);
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline size_t hash_val(const Types&... args)
{
int iIdx = 3;
printf("%d\n", iIdx);
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
class Cus
{
public:
std::size_t operator()(std::string & str1, std::string & str2, int & i3){
return hash_val(str1, str2, i3);
}
};
int main(void)
{
Cus f;
int i = 4;
size_t t = f(std::string("123"), std::string("abc"), i);
std::cin.get();
return t;
}
测试结果,与分析一致:
进一步,假如改写第一个hash_val函数,使其输入参数变为3个,则第二个hash_val只会调用一次。因为当args...包含两个参数时,第一个hash_val函数可以认为是一个特化函数,比第二个函数更匹配:
template <typename T1, typename T2>
inline void hash_val(size_t & seed, const T1 & val1, const T2 & val2)
{
int iIdx = 1;
printf("%d\n", iIdx);
hash_combine(seed, val1);
hash_combine(seed, val2);
}
检验: