如何为模板类内的枚举类实现通用format_as,避免重复包装?
解决模板类内部枚举的
format_as通用模板编译问题 你写的模板版format_as之所以编译失败,核心原因是typename S<T>::Side属于C++的非推导上下文。编译器没办法从传入的S<int>::Side这类枚举值,反向推导出模板参数T,导致libfmt在尝试匹配格式化函数时找不到合适的实例,最终触发编译错误。
下面提供两种可行的解决方案:
方案1:将format_as作为模板类的友元函数定义在类内部
这是最简洁的解决方式,利用ADL(依赖于参数的查找)让libfmt自动定位到对应枚举的格式化函数,完全避开模板推导问题:
#include <fmt/format.h> template<typename T> struct S { enum class Side { Left, Right }; // 每个T实例都会生成对应的format_as非模板函数 friend auto format_as(Side side) { switch(side) { case Side::Left: return "L"; case Side::Right: return "R"; } __builtin_unreachable(); } }; int main() { S<int>::Side s1 = S<int>::Side::Left; S<double>::Side s2 = S<double>::Side::Right; fmt::print("{} {}\n", s1, s2); // 输出 "L R" return 0; }
方案2:用类型特征关联枚举与模板参数T
如果不想把格式化逻辑放在类内部,可以定义类型特征模板,提前绑定枚举类型和对应的T,再实现通用format_as模板:
#include <fmt/format.h> #include <type_traits> template<typename T> struct S { enum class Side { Left, Right }; }; // 类型特征:从S<T>::Side提取对应的T template<typename E> struct side_to_T; template<typename T> struct side_to_T<typename S<T>::Side> { using type = T; }; template<typename E> using side_to_T_t = typename side_to_T<E>::type; // 通用format_as模板,推导枚举类型后提取T template<typename SideEnum> auto format_as(SideEnum side) { using T = side_to_T_t<SideEnum>; switch(side) { case S<T>::Side::Left: return "L"; case S<T>::Side::Right: return "R"; } __builtin_unreachable(); } int main() { S<int>::Side s1 = S<int>::Side::Left; S<double>::Side s2 = S<double>::Side::Right; fmt::print("{} {}\n", s1, s2); return 0; }
方案说明
- 方案1中,每个
S<T>实例都会生成一个专属的format_as(S<T>::Side)非模板函数,libfmt通过ADL机制可以直接匹配到该函数,无需模板推导。 - 方案2通过类型特征提前建立枚举类型与
T的映射,format_as的模板参数是可被推导的枚举类型,再通过特征提取出对应的T,解决了原模板无法推导T的问题。
内容的提问来源于stack exchange,提问作者NoSenseEtAl
相关产品推荐
相关产品推荐

