C++17下基于SFINAE为file_logger条件定义severity成员函数
为file_logger添加特定Severity的成员函数(带SFINAE编译时检查)
现有file_logger结构体实现
struct file_logger{ logger &__logger; const int32_t __log_fd; friend class logger; template <severity sev, typename... Args> inline void log(Args... args); template <severity sev, char sep, typename... Args> inline void log_s(int32_t log_fd, Args... args); private: file_logger(logger &l, const int32_t &fd) : __logger(l), __log_fd(fd) {} }; template <severity sev, typename... Args> inline void file_logger::log(Args... args){ __logger.log<sev>(__log_fd, args...); } template <severity sev, char sep, typename... Args> inline void file_logger::log_s(int32_t log_fd, Args... args){ __logger.log_s<sev, sep>(__log_fd, args...); }
需求说明
为避免每次编写log<severity::INFO>这类繁琐代码,需要给file_logger添加info、warn、error、debug等对应特定severity枚举值的快捷成员函数,示例如下:
/* 假设info已在file_logger中声明 */ template<typename... Args> inline void file_logger::info(Args... args){ __logger.log<severity::INFO>(__log_fd, args...); }
同时需满足以下编译时检查要求:
- 仅当调用对应
severity枚举不存在的成员函数时,触发编译错误; - 未被调用的无效模板函数会被编译器自动忽略;
- 基于C++17的SFINAE机制实现条件声明与定义。
解决方案
1. 定义编译期判断severity合法性的辅助工具
首先需要一个编译期常量函数,用来判断给定的severity枚举值是否有效。假设你的severity枚举定义如下(可根据实际情况调整):
#include <type_traits> enum class severity { INFO, WARN, ERROR, DEBUG }; // 编译期检查severity值是否合法 constexpr bool is_valid_severity(severity s) noexcept { switch(s) { case severity::INFO: case severity::WARN: case severity::ERROR: case severity::DEBUG: return true; default: return false; } }
2. 修改file_logger,添加带SFINAE检查的快捷成员函数
将每个快捷函数(如info、warn)声明为模板函数,通过std::enable_if_t控制其可见性:只有当对应的severity值合法时,该函数模板才会被编译器纳入候选;否则会被SFINAE机制排除。
struct file_logger{ logger &__logger; const int32_t __log_fd; friend class logger; template <severity sev, typename... Args> inline void log(Args... args); template <severity sev, char sep, typename... Args> inline void log_s(int32_t log_fd, Args... args); // INFO级别的快捷函数 template<typename... Args, std::enable_if_t<is_valid_severity(severity::INFO), int> = 0> inline void info(Args... args){ __logger.log<severity::INFO>(__log_fd, args...); } // WARN级别的快捷函数 template<typename... Args, std::enable_if_t<is_valid_severity(severity::WARN), int> = 0> inline void warn(Args... args){ __logger.log<severity::WARN>(__log_fd, args...); } // ERROR级别的快捷函数 template<typename... Args, std::enable_if_t<is_valid_severity(severity::ERROR), int> = 0> inline void error(Args... args){ __logger.log<severity::ERROR>(__log_fd, args...); } // DEBUG级别的快捷函数 template<typename... Args, std::enable_if_t<is_valid_severity(severity::DEBUG), int> = 0> inline void debug(Args... args){ __logger.log<severity::DEBUG>(__log_fd, args...); } private: file_logger(logger &l, const int32_t &fd) : __logger(l), __log_fd(fd) {} }; // 原模板函数的实现保持不变 template <severity sev, typename... Args> inline void file_logger::log(Args... args){ __logger.log<sev>(__log_fd, args...); } template <severity sev, char sep, typename... Args> inline void file_logger::log_s(int32_t log_fd, Args... args){ __logger.log_s<sev, sep>(__log_fd, args...); }
3. 扩展支持log_s版本(可选)
如果需要为log_s也添加对应severity的快捷函数,同样可以用SFINAE实现:
// 在file_logger结构体中添加以下内容: template<char sep = ' ', typename... Args, std::enable_if_t<is_valid_severity(severity::INFO), int> = 0> inline void info_s(Args... args){ __logger.log_s<severity::INFO, sep>(__log_fd, args...); } template<char sep = ' ', typename... Args, std::enable_if_t<is_valid_severity(severity::WARN), int> = 0> inline void warn_s(Args... args){ __logger.log_s<severity::WARN, sep>(__log_fd, args...); } // 同理添加error_s、debug_s...
实现原理
- 利用C++17的
std::enable_if_t结合编译期常量表达式is_valid_severity,实现SFINAE机制:当对应的severity值非法时,std::enable_if_t会导致模板参数推导失败,编译器自动忽略该函数模板; - 只有当调用某个快捷函数且对应的
severity枚举值存在时,函数模板才会被实例化;如果severity枚举中没有对应的取值,调用该快捷函数会触发编译错误——因为所有候选函数模板都被SFINAE排除,无匹配的函数可用。
内容的提问来源于stack exchange,提问作者Mahek Shamsukha
相关产品推荐
相关产品推荐

