函数重载是C++中实现静态多态的重要机制,在定义函数重载时,特别是在有函数模板加入时,更需要小心定义参数,防止歧义的出现
举例来说,有下面两个重载函数,用于执行数据库查询并返回查询结果,分别对应到查单个值和查列表两个版本,模板类型参数V是查询结果的类型:
template<class V>
long query(const std::string &strSql, V *pv, CDBConn *poConn=NULL);
template<class V>
long query(const std::string &strSql, long *plLen, V *pv, CDBConn *poConn=NULL);
实际以long类型调用第一个重载版本函数时:
CDBConn *pcon = NULL;
long lValue = 0;
query("select count(*) from tbl_data", &lValue, pcon);
编译器会提示歧义:
1>------ Build started: Project: test, Configuration: Debug Win32 ------
1>Compiling...
1>test.cpp
1>e:\cj\boost_test\test\test.cpp(321) : error C2668: 'query' : ambiguous call to overloaded function
1> e:\cj\boost_test\test\test.cpp(314): could be 'long query<CDBConn>(const std::string &,long *,V *,CDBConn *)'
1> with
1> [
1> V=CDBConn
1> ]
1> e:\cj\boost_test\test\test.cpp(310): or 'long query<long>(const std::string &,V *,CDBConn *)'
1> with
1> [
1> V=long
1> ]
1> while trying to match the argument list '(const char [1], long *, CDBConn *)'
1>Build log was saved at "file://e:\cj\boost_test\test\Debug\BuildLog.htm"
1>test - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
编译错误提示表示两个版本都能满足参数类型的匹配,即:
如果选择第一个版本,V的类型就被推导为long类型;而如果选择第二个版本,V的类型被推导为CDBConn类型,由于最后一个连接参数有缺省值可以不填
看来只需要将第二个重载版本的最后一个参数的缺省值去掉,不提供缺省值就可以解决这个问题了。
那有没有鱼和熊掌兼得的方法呢?
让第二个函数模板在V被推导成CDBConn时,不让自己生效不就行了!
这就是本文重点要介绍的函数模板隐藏技术了^^
原理:如果一个函数模板不能成功推导自己的所有模板参数,编译器就不会让它参与重载
提示:通过在函数的返回值类型上下工夫,就可以不修改函数参数的原型来达到我们的目的了。
到这里大家可以自己先想想实现方案,实现代码稍后附上^^
-------------------------------------------------------------------------------
实现代码:
按如下代码修改第二个重载版本的返回值类型:
template<class V> typename enable_if_not_conn_type<V>::type
query(const std::string &strSql, long *plLen, V *pv, CDBConn *poConn=NULL);
上面模板enable_if_not_conn_type对类型CDBConn的特化版本无法推导出type类型:
template <class T>
struct enable_if_not_conn_type
{
typedef long type;
};
template <>
struct enable_if_not_conn_type<CDBConn> //特化版本没有定义type类型
{
};
更通用的模板定义如下:
template <class T1, class T2>
struct enable_if_not_type
{
typedef long type;
};
template <class T>
struct enable_if_not_type<T,T>
{
};
使用上面模板,重新定义函数:
template<class V> typename enable_if_not_type<V,CDBConn>::type
query(const std::string &strSql, long *plLen, V *pv, CDBConn *poConn=NULL);