C++20同时定义两类fmt::formatter模板出现重定义错误求解
你遇到的重定义问题本质是两个独立的语法、编译器特性问题共同导致的:
1. CharArray概念的写法本身有语法错误
你给CharArray概念写了两层template头:
template<std::size_t arrayLen> template<typename T> concept CharArray = std::is_base_of_v<std::array<char, arrayLen>, T>;
这种写法定义的是需要先传长度参数、再传类型参数的多参数概念,你直接用CharArray T约束模板参数的时候,根本没给concept传必要的arrayLen参数,属于语法错误。GCC 11对concept的错误诊断做得不好,没有第一时间抛出这个错,反而把两个fmt::formatter的偏特化都识别成了无有效约束的模板,自然就会报重定义。
正确的无长度依赖的CharArray概念可以这么写:
template<typename T> concept CharArray = requires (T t) { []<std::size_t N>(std::array<char, N>&){}(t); };
也可以自己写type trait判断T是不是std::array<char, N>的派生类。
2. 类模板偏特化的约束匹配规则和函数重载不一样
你测试普通函数重载没问题,是因为C++函数重载有完整的决议规则,多个约束都满足的时候能选到更匹配的重载。但类模板偏特化没有这套逻辑,标准要求多个偏特化之间必须互斥:也就是对任意一个类型T,最多只能有一个偏特化能匹配。如果你没保证Exception和CharArray两个concept完全互斥(不可能有类型同时满足两个约束),两个偏特化就会被判定为等价,触发重定义错误。
3. 额外的GCC 11兼容问题
GCC 11有个已知的约束偏特化识别bug:如果你在fmt命名空间外面定义带concept约束的formatter偏特化,编译器可能丢失约束信息,导致特化匹配失效。要解决这个问题,得把两个formatter的特化定义都放到namespace fmt { ... }块里面。
内容的提问来源于stack exchange,提问作者JimPri
相关产品推荐
相关产品推荐

