C++偏特化中可变参模板模板参数精确匹配规则咨询
结论
- GCC 12.1的行为完全符合C++17及之后版本的标准规定,使用非可变参模板模板参数的偏特化写法属于良构代码。
- Clang 14.0.0的输出是实现bug,源于其对模板模板参数推导规则的实现未完全对齐C++17标准,该问题在Clang 15及后续版本中已修复。
分歧产生的根本原因
两款编译器的行为差异,本质是C++17对模板模板参数匹配规则的重大修改,加上旧版本Clang的实现滞后导致的:
- C++17前的旧规则:模板模板实参必须和模板模板形参的形参列表完全精确匹配,包括参数数量、是否包含参数包、默认参数配置。按此规则,
template<typename> typename V完全无法匹配可变参模板std::variant,偏特化推导会直接失败,所有类型都会匹配主模板,输出两行I am other type,这正是Clang 14表现出的行为。 - C++17生效的新规则:P0522R0提案正式纳入标准后,模板模板参数匹配不再要求形参和实参的模板形参列表完全一致,只要模板模板实参(此处为
std::variant)能够接受模板模板形参要求的最少参数,即判定为匹配成功。匹配完成后,后续实例化该模板模板形参(代码中的V)时,传入的参数数量不受模板模板形参的形参列表限制,只要实际绑定的模板支持传入的参数即可。
回到修改模板头后的代码场景:
- 传入
TypeChecker<std::variant<int, float>>时,偏特化首先做模板参数推导:std::variant是可变参模板,天然支持传入单个类型参数,满足template<typename> typename V的匹配要求,因此V被成功推导为std::variant。 - 接着推导参数包
Ts...:V<Ts...>需要匹配std::variant<int, float>,因此Ts...被正确推导为int, float,随后检查requires约束:std::same_as<V<Ts...>, std::variant<Ts...>>等价于std::same_as<std::variant<int, float>, std::variant<int, float>>,结果为true,偏特化匹配成功,输出I am std::variant。 - 传入
TypeChecker<int>时,不满足偏特化的匹配形式,走主模板输出I am other type,和GCC的输出一致。
Clang 14的错误在于推导V<Ts...>实例化参数时,仍沿用旧规则逻辑,错误地将模板模板形参的形参列表作为实例化时的参数数量限制,认为声明为template<typename> typename V的V只能传入1个类型参数,因此遇到两个模板参数的std::variant<int, float>时直接判定偏特化推导失败,回退到主模板,才输出了两行I am other type。
兼容性建议
如果需要兼容Clang 14及更老版本,保留模板模板参数的可变参写法(即template<typename...> typename V)是兼容性最好的方案,不会出现编译器行为分歧。
内容的提问来源于stack exchange,提问作者Jakob Stark
相关产品推荐
相关产品推荐

