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类型,应格外注意!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值