跳到主要内容C++20 概念(Concepts):模板参数的语义约束定义 | 极客日志C++算法
C++20 概念(Concepts):模板参数的语义约束定义
C++20 Concepts 是用于为模板参数定义语义约束的编译期谓词系统。本文介绍了概念的基本定义语法、组合方式及标准库预定义概念。详细讲解了 requires 表达式的四种约束要求:简单要求、类型要求、复合要求和嵌套要求,并结合代码示例说明了其在验证表达式有效性、返回类型及异常规范方面的应用。旨在帮助开发者实现从“语法兼容”到“语义契约”的转变,提升泛型编程的可读性与错误诊断能力。
C++20 概念(Concepts):模板参数的语义约束定义
什么是概念
概念是一套为模板参数定义语义约束的声明式工具。通过 concept 关键字,它将一组类型必须满足的编译期谓词封装为一个命名的语义契约。其本质是提升模板从'语法兼容'到'语义契约'的编译期接口系统。
它将类型需求从隐式的、被动的'鸭子类型'检测,转变为显式的、主动的、由编译器在实例化前验证的设计约束。核心理念是'约束即文档,文档可执行'。
示例
< T>
Integral = std::is_integral_v<T>;
{
a + b;
}
(, );
(, );
template
typename
concept
template<Integral T>
T add(T a, T b)
return
add
5
3
add
5.5
3.3
概念用于指定模板参数必须满足的要求,它是类型和值的约束集合。
概念的定义语法
这里的'约束'可以是 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>;
标准库提供的预定义概念(常用部分)
1. 基础类型概念
std::integral<T>:整数类型
std::signed_integral<T>:有符号整数类型
std::unsigned_integral<T>:无符号整数类型
std::floating_point<T>:浮点类型
std::arithmetic<T>:整数或浮点类型
std::fundamental<T>:基础类型(算术、void、nullptr_t)
2. 类型关系概念
std::same_as<T, U>:T 和 U 是相同类型
std::derived_from<T, U>:T 公开继承自 U
std::convertible_to<T, U>:T 可转换为 U
std::common_reference_with<T, U>:有共同引用类型
std::common_with<T, U>:有共同类型
3. 比较概念
std::equality_comparable<T>:可比较相等(==, !=)
std::equality_comparable_with<T, U>:可与 U 比较相等
std::totally_ordered<T>:全序(==, !=, <, >, <=, >=)
std::totally_ordered_with<T, U>:可与 U 全序比较
4. 对象概念
std::movable<T>:可移动构造和赋值
std::copyable<T>:可复制构造和赋值
std::semiregular<T>:可默认构造且可复制
std::regular<T>:正规类型(可默认构造、可复制、可比较相等)
std::swappable<T>:可交换
std::swappable_with<T, U>:T 和 U 可互相交换
5. 可调用概念
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>:严格弱序
6. 迭代器类别概念
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>:连续迭代器
7. 迭代器属性概念
std::indirectly_readable<I>:可间接读取
std::indirectly_writable<I, T>:可间接写入类型 T
std::weakly_incrementable<I>:可弱递增
std::incrementable<I>:可递增
std::indirectly_movable<I1, I2>:可从 I1 移动到 I2
std::indirectly_copyable<I1, I2>:可从 I1 复制到 I2
8. 迭代器操作概念
std::indirectly_swappable<I1, I2>:可间接交换
std::indirectly_comparable<I1, I2, R>:可间接比较
std::permutable<I>:可置换
std::mergeable<I1, I2, O, R>:可合并
std::sortable<I, R>:可排序
9. 范围概念
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>:连续范围
10. 范围适配器概念
std::ranges::viewable_range<R>:可转换为视图的范围
requires 表达式语法说明
1. requires 表达式的基本语法结构
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
(1)简单要求 (Simple Requirements)
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>();
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 检查。
(2)类型要求 (Type Requirements)
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>;
};
特性: 验证类型名在给定上下文中有效;支持模板嵌套类型检查;支持类型别名检查;支持复杂类型表达式。
(3)复合要求 (Compound Requirements)
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>;
{ container.clear() } noexcept;
{ container.swap(std::declval<T&>()) } noexcept;
{ container.push_back(std::declval<typename T::value_type>()) } noexcept -> std::same_as<void>;
{ container.reserve(0) } -> std::same_as<void>;
{ 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>;
{ 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;类型约束支持任意概念;支持复杂的类型表达式。
(4)嵌套要求 (Nested Requirements)
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 { 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*>);
};
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]) { }
template<typename... Ts>
concept VariadicConcept = requires(Ts... args) {
(args + ...);
};
requires 表达式的求值规则
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 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown 转 HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
- HTML 转 Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online