C++20模板参数包无法实现概念包含?原因及替代方案
C++20概念包含与模板参数包特化问题
背景
在C++20中,**概念包含(concept subsumption)**指一个概念是另一个概念的超集。例如BetterFooable是Fooable的子集,满足前者的类型必然满足后者:
template <typename T> concept Fooable = requires(T& t) { t.foo(); }; template <typename T> concept BetterFooable = Fooable<T> && requires(T& t, int x) { t.foo(x); };
对于单个模板参数的类特化,编译器能正确识别这种包含关系:
template <Fooable T1> struct MyStruct1 {}; template <BetterFooable T1> struct MyStruct1<T1> {};
但当使用模板参数包时,GCC和Clang会拒绝如下特化(仅MSVC支持):
template <Fooable... Ts> struct MyStruct2 {}; template <BetterFooable... Ts> struct MyStruct2<Ts...> {};
GCC报错信息如下:
:15:8: error: partial specialization 'struct MyStruct2 ' does not specialize any template arguments and is not more constrained than the primary template; to define the primary template, remove the template argument list 15 | struct MyStruct2 {}; | ^~~~~~~~~~~~~~~~ :12:8: note: primary template here 12 | struct MyStruct2 {}; | ^~~~~~~~~
GCC和Clang的行为是否符合标准?
符合标准。根据C++20规范,部分特化要求特化的约束必须比主模板更严格,但标准并未将单元素的概念包含关系扩展到整个参数包的约束匹配中。
主模板的约束是Fooable... Ts,特化的约束是BetterFooable... Ts——虽然每个BetterFooable<T>包含Fooable<T>,但标准不认为这种逐个元素的包含等价于整个参数包约束的包含关系。因此编译器判定该特化并未"更受约束",进而拒绝代码。
替代实现方案
可以通过以下两种方式实现参数包的概念包含特化:
方案1:定义针对参数包的全局概念
将参数包的整体约束封装为独立概念,让编译器能识别包含关系:
template <typename... Ts> concept AllFooable = (Fooable<Ts> && ...); template <typename... Ts> concept AllBetterFooable = (BetterFooable<Ts> && ...); template <AllFooable... Ts> struct MyStruct2 {}; template <AllBetterFooable... Ts> struct MyStruct2<Ts...> {};
方案2:在特化中使用requires子句显式约束
直接在特化的模板声明后添加requires子句,明确约束参数包的每个元素:
template <Fooable... Ts> struct MyStruct2 {}; template <typename... Ts> requires (BetterFooable<Ts> && ...) struct MyStruct2<Ts...> {};
两种方案都能让GCC和Clang正确识别特化的约束比主模板更严格,从而通过编译。
内容的提问来源于stack exchange,提问作者Bernard
相关产品推荐
相关产品推荐

