C++模板函數調用順序

上一篇簡單演示了一下模板函數的打印輸出功能

但是,如果同時存在

// 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類型,應格外注意!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章