C++可变参数模板log函数重载歧义问题求解
解决ClogLogger::log模板重载歧义问题
问题根源
调用log(0, "{} {}", "foo_1", "foo_2")时,编译器可将第二个字符串参数推导为带异常重载的exceptionType模板参数,此时两个重载都能匹配参数列表,导致错误选择带异常的版本,进而因字符串无what()成员函数引发调用错误。
解决方案
通过SFINAE机制限制带异常的重载仅接受std::exception或其子类类型,让字符串字面量无法匹配该重载,编译器会自动选择不带异常的版本。
方法1:C++17及更早版本(使用std::enable_if)
需包含<type_traits>头文件,修改带异常的log函数:
#include <type_traits> // 不带异常的log函数(保持不变) template<typename levelType, typename formatType, typename ... variadicParamsType> void ClogLogger::log(levelType level, formatType format, variadicParamsType... formatParams) { string levelHeader = getLevelHeader(level); clog << levelHeader << fmt::format(format, formatParams...) << endl; } // 带异常的log函数(添加SFINAE约束) template<typename levelType, class exceptionType, typename formatType, typename ... variadicParamsType> std::enable_if_t<std::is_base_of_v<std::exception, std::decay_t<exceptionType>>> ClogLogger::log(levelType level, exceptionType e, formatType format, variadicParamsType ... formatParams) { string levelHeader = getLevelHeader(level); clog << levelHeader << fmt::format(format, formatParams...) << endl; THROW_MYEXCEPTION(e.what());// 自定义宏 }
方法2:C++20及以上版本(使用requires表达式)
requires表达式让约束更直观,代码可读性更高:
#include <type_traits> // 不带异常的log函数(保持不变) template<typename levelType, typename formatType, typename ... variadicParamsType> void ClogLogger::log(levelType level, formatType format, variadicParamsType... formatParams) { string levelHeader = getLevelHeader(level); clog << levelHeader << fmt::format(format, formatParams...) << endl; } // 带异常的log函数(添加requires约束) template<typename levelType, class exceptionType, typename formatType, typename ... variadicParamsType> void ClogLogger::log(levelType level, exceptionType e, formatType format, variadicParamsType ... formatParams) requires std::is_base_of_v<std::exception, std::decay_t<exceptionType>> { string levelHeader = getLevelHeader(level); clog << levelHeader << fmt::format(format, formatParams...) << endl; THROW_MYEXCEPTION(e.what());// 自定义宏 }
原理说明
std::is_base_of_v<std::exception, std::decay_t<exceptionType>>用于检查exceptionType是否是std::exception的子类(std::decay_t处理可能的引用/const限定符)。- 当传入参数不是异常类型时,带异常的重载会因模板替换失败被排除出候选函数列表,编译器会自动选择不带异常的版本。
内容的提问来源于stack exchange,提问作者AdCerros
相关产品推荐
相关产品推荐

