C++20中如何编写Concept以匹配含模板参数的多类型集合
如何编写匹配含模板参数的模板类的Concept?
我是C++新手,正在学习Concept的基础知识。希望编写一个函数模板,使其可接受几种类型的参数,其中包含带模板参数的模板类。请问如何编写这类Concept?
示例代码
#include <iostream> template <typename T, int N> struct Foo { T exampleWithParameters[N]{}; void print() { std::cout << "is a Foo\n"; } }; struct Bar1 { void print() { std::cout << "is a Bar1\n"; } }; struct Bar2 { void print() { std::cout << "is a Bar2\n"; } }; template <typename T, typename... U> concept IsAnyOf = (std::same_as<T, U> || ...);
我的尝试代码(无法运行)
template <typename T, int N, typename U> concept DesiredType = IsAnyOf<T, Bar1, Bar2, Foo<U, N>>; template <int N, typename U> void func(DesiredType<N, U> auto x) { x.print(); } int main() { Foo<int,3> x; func(x); }
另一种同样无法运行的写法:
template <typename T, int N, typename U> void func(T x) { static_assert(DesiredType<T, N, U>); x.print(); }
问题分析与正确写法
你之前的写法核心问题在于:DesiredType 需要额外指定 N 和 U,但编译器无法从传入的类型(比如 Foo<int,3>)自动推导这两个参数。正确的思路是让Concept仅接收待判断的类型 T,内部判断 T 是否是目标类型之一(包括任意实例化的 Foo)。
步骤1:编写判断是否为Foo实例的Concept
可以通过模板特化的辅助类实现,或者利用C++20的模板lambda:
方法一:模板特化辅助类
#include <type_traits> // 辅助模板,默认匹配非Foo类型 template<typename> struct IsFooHelper : std::false_type {}; // 特化匹配任意Foo<U,N> template<typename U, int N> struct IsFooHelper<Foo<U, N>> : std::true_type {}; // 定义IsFoo Concept template<typename T> concept IsFoo = IsFooHelper<T>::value;
方法二:C++20模板lambda(更简洁)
template<typename T> concept IsFoo = requires(T t) { // 通过模板lambda检查T是否能隐式转换为Foo<U,N>(仅当T本身是Foo实例时成立) []<typename U, int N>(Foo<U, N>){}(t); };
步骤2:组合目标类型的Concept
将Bar1、Bar2和任意Foo实例组合起来:
template<typename T> concept DesiredType = IsAnyOf<T, Bar1, Bar2> || IsFoo<T>;
步骤3:编写符合要求的函数模板
现在可以编写简洁的函数模板,编译器会自动推导并验证类型:
// 写法一:requires子句 template<typename T> requires DesiredType<T> void func(T x) { x.print(); } // 写法二:缩写形式(C++20支持) void func(DesiredType auto x) { x.print(); }
测试代码
int main() { Foo<int,3> foo; Bar1 bar1; Bar2 bar2; func(foo); // 输出:is a Foo func(bar1); // 输出:is a Bar1 func(bar2); // 输出:is a Bar2 // 以下代码会编译失败(符合预期) // int invalid; // func(invalid); return 0; }
为什么之前的写法失败?
之前的DesiredType将N和U作为独立模板参数,但编译器无法从T(比如Foo<int,3>)反向推导出这两个参数。Concept的参数推导是独立的,不能依赖待判断类型的内部模板参数,必须通过Concept内部的逻辑提取或匹配这些信息。
内容的提问来源于stack exchange,提问作者Argyll
相关产品推荐
相关产品推荐

