C++20 概念(Concepts):模板参数的语义约束定义
什么是概念
概念是一套为模板参数定义语义约束的声明式工具。通过 concept 关键字,它将一组类型必须满足的编译期谓词封装为一个命名的语义契约。其本质是提升模板从'语法兼容'到'语义契约'的编译期接口系统。
它将类型需求从隐式的、被动的'鸭子类型'检测,转变为显式的、主动的、由编译器在实例化前验证的设计约束。核心理念是'约束即文档,文档可执行'。
C++20 Concepts 是用于为模板参数定义语义约束的编译期谓词系统。概念的基本定义语法、组合方式及标准库预定义概念。详细讲解了 requires 表达式的四种约束要求:简单要求、类型要求、复合要求和嵌套要求,并结合代码示例说明了其在验证表达式有效性、返回类型及异常规范方面的应用。旨在帮助开发者实现从“语法兼容”到“语义契约”的转变,提升泛型编程的可读性与错误诊断能力。
概念是一套为模板参数定义语义约束的声明式工具。通过 concept 关键字,它将一组类型必须满足的编译期谓词封装为一个命名的语义契约。其本质是提升模板从'语法兼容'到'语义契约'的编译期接口系统。
它将类型需求从隐式的、被动的'鸭子类型'检测,转变为显式的、主动的、由编译器在实例化前验证的设计约束。核心理念是'约束即文档,文档可执行'。
// 定义一个概念
template<typename T>
concept Integral = std::is_integral_v<T>;
// 使用概念
template<Integral T>
T add(T a, T b) {
return a + b;
}
add(5, 3); // 正确
add(5.5, 3.3); // 编译错误
概念用于指定模板参数必须满足的要求,它是类型和值的约束集合。
概念的基本用法是先定义,然后使用。
concept myconcept = 约束;
这里的'约束'可以是 requires 表达式,也可以是标准库提供的概念支持。
template<typename T>
concept Drawable = requires(T t) {
t.draw();
};
template<typename T>
concept Integral = std::is_integral_v<T>;
template<typename T>
concept Printable = requires(T t) {
{ std::cout << t } -> std::same_as<std::ostream&>;
};
template<typename T>
concept Serializable = requires(T t) {
t.serialize();
T::deserialize;
};
// 组合多个概念
template<typename T>
concept DataObject = Printable<T> && Serializable<T>;
std::integral<T>:整数类型std::signed_integral<T>:有符号整数类型std::unsigned_integral<T>:无符号整数类型std::floating_point<T>:浮点类型std::arithmetic<T>:整数或浮点类型std::fundamental<T>:基础类型(算术、void、nullptr_t)std::same_as<T, U>:T 和 U 是相同类型std::derived_from<T, U>:T 公开继承自 Ustd::convertible_to<T, U>:T 可转换为 Ustd::common_reference_with<T, U>:有共同引用类型std::common_with<T, U>:有共同类型std::equality_comparable<T>:可比较相等(==, !=)std::equality_comparable_with<T, U>:可与 U 比较相等std::totally_ordered<T>:全序(==, !=, <, >, <=, >=)std::totally_ordered_with<T, U>:可与 U 全序比较std::movable<T>:可移动构造和赋值std::copyable<T>:可复制构造和赋值std::semiregular<T>:可默认构造且可复制std::regular<T>:正规类型(可默认构造、可复制、可比较相等)std::swappable<T>:可交换std::swappable_with<T, U>:T 和 U 可互相交换std::invocable<F, Args...>:可用 Args... 调用std::regular_invocable<F, Args...>:正规可调用(不修改函数对象)std::predicate<F, Args...>:返回 bool 的可调用std::relation<R, T, U>:二元关系(返回 bool)std::equivalence_relation<R, T, U>:等价关系std::strict_weak_order<R, T, U>:严格弱序std::input_iterator<I>:输入迭代器std::output_iterator<I, T>:输出迭代器(可写入类型 T)std::forward_iterator<I>:前向迭代器std::bidirectional_iterator<I>:双向迭代器std::random_access_iterator<I>:随机访问迭代器std::contiguous_iterator<I>:连续迭代器std::indirectly_readable<I>:可间接读取std::indirectly_writable<I, T>:可间接写入类型 Tstd::weakly_incrementable<I>:可弱递增std::incrementable<I>:可递增std::indirectly_movable<I1, I2>:可从 I1 移动到 I2std::indirectly_copyable<I1, I2>:可从 I1 复制到 I2std::indirectly_swappable<I1, I2>:可间接交换std::indirectly_comparable<I1, I2, R>:可间接比较std::permutable<I>:可置换std::mergeable<I1, I2, O, R>:可合并std::sortable<I, R>:可排序std::ranges::range<R>:范围(有 begin() 和 end())std::ranges::sized_range<R>:有大小信息的范围std::ranges::view<R>:视图(轻量、非拥有范围)std::ranges::borrowed_range<R>:借用范围std::ranges::input_range<R>:输入范围std::ranges::forward_range<R>:前向范围std::ranges::bidirectional_range<R>:双向范围std::ranges::random_access_range<R>:随机访问范围std::ranges::contiguous_range<R>:连续范围std::ranges::viewable_range<R>:可转换为视图的范围// 基本形式
requires (参数列表 (可选)) {
要求序列
}
// 完整语法图解
requires-expression:
requires ( parameter-declaration-clause(opt) ) {
requirement-seq
}
requirement-seq:
requirement requirement-seq
requirement
requirement:
simple-requirement // 简单要求
type-requirement // 类型要求
compound-requirement // 复合要求
nested-requirement // 嵌套要求
验证表达式是否有效(是否能编译通过)。
template<typename T>
concept HasMethods = requires(T obj) {
// 验证成员函数存在
obj.method1();
obj.method2();
// 验证操作符存在
obj + obj;
-obj;
// 验证静态成员存在
T::static_method();
// 验证友元函数存在
std::cout << obj;
// 验证类型转换存在
static_cast<double>(obj);
// 验证模板成员函数
obj.templatemethod<int>();
// 验证异常规范(C++20 起)
noexcept(obj.clear());
};
struct Example {
void method1() {}
void method2() {}
Example operator+(const Example&) const { return *this; }
Example operator-() const { return *this; }
static void static_method() {}
template<typename U> void method() {}
void clear() noexcept {}
};
static_assert(HasMethods<Example>);
特性: 只检查表达式是否合法,不检查返回类型;表达式中的类型必须完全解析;支持模板成员函数检查;支持 noexcept 检查。
验证类型名是否有效。
template<typename T>
concept HasNestedTypes = requires {
// 验证嵌套类型存在
typename T::value_type;
typename T::iterator;
typename T::const_iterator;
typename T::difference_type;
typename T::size_type;
// 验证模板嵌套类型
typename T::template rebind<double>::other;
// 验证类型别名
typename std::add_const_t<T>;
typename std::remove_reference_t<T>;
// 验证复杂类型表达式
typename std::conditional_t<sizeof(T)==4, int, double>;
typename decltype(T{})::value_type;
};
// 带有类型检查的容器概念
template<typename T>
concept AllocatorAwareContainer = requires {
typename T::value_type;
typename T::allocator_type;
typename T::size_type;
typename T::difference_type;
typename T::reference;
typename T::const_reference;
typename T::pointer;
typename T::const_pointer;
typename T::iterator;
typename T::const_iterator;
typename T::reverse_iterator;
typename T::const_reverse_iterator;
requires std::same_as<typename T::allocator_type::value_type, typename T::value_type>;
};
// 智能指针类型检查
template<typename Ptr>
concept SmartPointer = requires {
typename Ptr::element_type;
typename std::add_pointer_t<typename Ptr::element_type>;
};
特性: 验证类型名在给定上下文中有效;支持模板嵌套类型检查;支持类型别名检查;支持复杂类型表达式。
验证表达式并检查返回类型和异常规范。
/* 语法:{ 表达式 } noexcept(可选) -> 类型约束 ; */
template<typename T>
concept CompleteContainer = requires(T container) {
// 基本检查:表达式有效,不检查返回类型
{ container.begin() };
{ container.end() };
// 检查返回特定类型
{ container.size() } -> std::same_as<typename T::size_type>;
{ container.empty() } -> std::convertible_to<bool>;
{ container.max_size() } -> std::same_as<typename T::size_type>;
// 检查返回类型可转换
{ container[0] } -> std::convertible_to<typename T::reference>;
{ container.at(0) } -> std::convertible_to<typename T::reference>;
{ container.front() } -> std::convertible_to<typename T::reference>;
{ container.back() } -> std::convertible_to<typename T::reference>;
// 检查 noexcept
{ container.clear() } noexcept;
{ container.swap(std::declval<T&>()) } noexcept;
// 组合检查:noexcept + 类型约束
{ container.push_back(std::declval<typename T::value_type>()) } noexcept -> std::same_as<void>;
// 检查可能抛出的操作
{ container.reserve(0) } -> std::same_as<void>;
// 使用 decltype 的复杂类型约束
{ container.data() } -> std::same_as<std::add_pointer_t<typename T::value_type>>;
// 检查成员函数指针
{ &T::size } -> std::same_as<typename T::size_type(T::*)() const>;
};
// 数学类型概念
template<typename T>
concept Arithmetic = requires(T a, T b) {
// 算术运算返回相同类型
{ a + b } -> std::same_as<T>;
{ a - b } -> std::same_as<T>;
{ a * b } -> std::same_as<T>;
{ a / b } -> std::same_as<T>;
{ -a } -> std::same_as<T>;
{ +a } -> std::same_as<T>;
// 比较运算返回 bool
{ a == b } -> std::convertible_to<bool>;
{ a != b } -> std::convertible_to<bool>;
{ a < b } -> std::convertible_to<bool>;
{ a > b } -> std::convertible_to<bool>;
{ a <= b } -> std::convertible_to<bool>;
{ a >= b } -> std::convertible_to<bool>;
// 复合赋值
{ a += b } -> std::same_as<T&>;
{ a -= b } -> std::same_as<T&>;
{ a *= b } -> std::same_as<T&>;
{ a /= b } -> std::same_as<T&>;
// 自增自减
{ ++a } -> std::same_as<T&>;
{ a++ } -> std::same_as<T>;
{ --a } -> std::same_as<T&>;
{ a-- } -> std::same_as<T>;
};
// 函数对象概念
template<typename F, typename... Args>
concept RegularInvocableWith = requires(F f, Args... args) {
{ std::invoke(f, args...) } noexcept(noexcept(std::invoke(f, args...))) -> std::same_as<std::invoke_result_t<F, Args...>>;
};
// 迭代器概念
template<typename I>
concept DereferenceableIterator = requires(I it) {
{ *it } -> std::same_as<std::iter_reference_t<I>>;
{ it.operator->() } -> std::same_as<std::add_pointer_t<std::remove_reference_t<std::iter_reference_t<I>>>>;
{ it[0] } -> std::same_as<std::iter_reference_t<I>>;
};
特性: 检查表达式有效性;可选检查返回类型;可选检查 noexcept;类型约束支持任意概念;支持复杂的类型表达式。
在 requires 表达式中添加额外的约束。
template<typename T>
concept ComplexType = requires(T obj) {
// 简单要求
obj.operation();
// 类型要求
typename T::value_type;
// 嵌套要求:编译时布尔条件
requires sizeof(T) <= 64;
requires alignof(T) <= 8;
requires !std::is_polymorphic_v<T>;
requires std::is_trivially_copyable_v<T>;
// 嵌套要求:概念检查
requires std::default_initializable<T>;
requires std::copyable<T>;
requires std::movable<T>;
requires std::totally_ordered<T>;
// 嵌套要求:常量表达式
requires T::max_size > 0;
requires (T::version == 1 || T::version == 2);
// 嵌套要求:类型特性
requires std::is_nothrow_destructible_v<T>;
requires std::has_virtual_destructor_v<T>;
// 嵌套要求:依赖其他 requires 表达式
requires requires { obj.nested_operation(); };
requires requires(T a, T b) { a == b; };
// 嵌套要求:逻辑组合
requires (std::integral<typename T::value_type> || std::floating_point<typename T::value_type>);
requires std::same_as<decltype(obj.get()), typename T::value_type*>;
// 嵌套要求:编译时计算
requires (sizeof(typename T::value_type) * T::element_count <= 1024);
// 嵌套要求:基于模板参数的约束
requires (std::rank_v<T> == 1);
// 嵌套要求:用户定义类型特性
requires HasTypeTrait<T>::value;
// 嵌套要求:可变参数模板支持
requires (std::is_constructible_v<T, int, double, const char*>);
};
// 高级示例:SFINAE 友好概念
template<typename T>
concept Advanced = requires {
requires requires { typename std::tuple_size<T>::type; typename std::tuple_element_t<0, T>; };
requires std::is_aggregate_v<T>;
requires (std::tuple_size_v<T> > 0);
requires requires { T::reflect(); { T::meta_info } -> std::same_as<const char*>; };
requires requires(T obj, std::ostream& os) {
{ obj.serialize(os) } -> std::same_as<std::ostream&>;
};
requires requires(T obj, std::istream& is) {
{ T::deserialize(is) } -> std::same_as<T>;
};
};
// 编译时优化提示概念
template<typename T>
concept Optimizable = requires {
requires std::is_trivial_v<T>;
requires std::is_standard_layout_v<T>;
requires (sizeof(T) % alignof(T) == 0);
requires std::is_trivially_copyable_v<T>;
requires std::is_trivially_destructible_v<T>;
requires !std::is_polymorphic_v<T>;
requires !std::has_virtual_destructor_v<T>;
requires std::is_trivially_copy_constructible_v<T>;
requires std::is_trivially_move_constructible_v<T>;
requires std::is_trivially_copy_assignable_v<T>;
requires std::is_trivially_move_assignable_v<T>;
};
// 内存安全概念
template<typename T>
concept MemorySafe = requires {
requires std::is_nothrow_default_constructible_v<T>;
requires std::is_nothrow_copy_constructible_v<T>;
requires std::is_nothrow_move_constructible_v<T>;
requires std::is_nothrow_copy_assignable_v<T>;
requires std::is_nothrow_move_assignable_v<T>;
requires std::is_nothrow_destructible_v<T>;
requires std::is_nothrow_swappable_v<T>;
requires (std::is_nothrow_constructible_v<T, int>);
};
特性: 在 requires 表达式内部添加额外约束;支持任意布尔常量表达式;支持概念检查;支持类型特性检查;支持逻辑组合(&&, ||, !);支持依赖其他 requires 表达式。
// 基本形式
requires(T t) { /* ... */ }
// 多个参数
requires(T t, U u, int n) { /* ... */ }
// 引用参数
requires(T& ref, const T& cref) { /* ... */ }
// 指针参数
requires(T* ptr, const T* cptr) { /* ... */ }
// 右值引用参数
requires(T&& rref) { /* ... */ }
// 数组参数
requires(T arr[10]) { /* ... */ }
// 可变参数模板(C++20 起有限支持)
template<typename... Ts>
concept VariadicConcept = requires(Ts... args) {
(args + ...);
};
// 默认参数(不允许)
// requires (T t = T{}) { /* ... */ } // 错误!
template<typename T>
concept OrderedEvaluation = requires(T t) {
requires std::default_initializable<T>;
requires std::copyable<T>;
{ t.operation2() } -> std::same_as<int>;
requires requires { t.operation3(); };
};
按顺序求值,短路求值。如果前面的约束失败,后续的约束将不再求值。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online