上一篇簡單演示了一下模板函數的打印輸出功能
但是,如果同時存在
// 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類型,應格外注意!!!