CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
🔥C++面试
  • 第1章 C++ 惯用法与Modern C++篇
  • 第2章 C++开发工具与调试进阶
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 网络通信协议设计
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 服务其他模块设计
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 🔥C++游戏编程入门(零基础学C++)
  • 🔥使用C++17从零开发一个调试器 (opens new window)
  • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
  • 🔥使用C++从零写一个C语言编译器 (opens new window)
  • 🔥从零用C语言写一个Redis
  • leveldb源码分析
  • libevent源码分析
  • Memcached源码分析
  • TeamTalk源码分析
  • 优质源码分享 (opens new window)
  • 🔥远程控制软件gh0st源码分析
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • 高效Go并发编程
  • Go性能调优
  • Go项目架构设计
  • 🔥使用Go从零开发一个数据库
  • 🔥使用Go从零开发一个编译器 (opens new window)
  • 🔥使用Go从零开发一个解释器 (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
🔥C++面试
  • 第1章 C++ 惯用法与Modern C++篇
  • 第2章 C++开发工具与调试进阶
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 网络通信协议设计
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 服务其他模块设计
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 🔥C++游戏编程入门(零基础学C++)
  • 🔥使用C++17从零开发一个调试器 (opens new window)
  • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
  • 🔥使用C++从零写一个C语言编译器 (opens new window)
  • 🔥从零用C语言写一个Redis
  • leveldb源码分析
  • libevent源码分析
  • Memcached源码分析
  • TeamTalk源码分析
  • 优质源码分享 (opens new window)
  • 🔥远程控制软件gh0st源码分析
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • 高效Go并发编程
  • Go性能调优
  • Go项目架构设计
  • 🔥使用Go从零开发一个数据库
  • 🔥使用Go从零开发一个编译器 (opens new window)
  • 🔥使用Go从零开发一个解释器 (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • C++20 完全指南 说明
  • 第1章 比较和<=>运算符
  • 第2章 函数参数的占位符类型
  • 第3章 概念、要求和约束
  • 第4章 概念、需求和约束详解
  • 第5章 标准概念详解
    • 5.1 所有标准概念概述
      • 5.1.1 头文件和命名空间
      • 5.1.2 标准概念的包含关系
    • 5.2 与语言相关的概念
      • 5.2.1 算术概念
      • std::integral<T\>
      • std::signed_integral<T\>
      • std::unsigned_integral<T\>
      • std::floating_point<T\>
      • 5.2.2 对象概念
      • std::movable<T\>
      • 5.2.3 类型间关系的概念
      • 5.2.4 比较概念
    • 5.3 迭代器和范围的概念
      • 5.3.1 范围和视图的概念
      • 5.3.2 针对类似指针对象的概念
      • 间接支持的基本概念
      • 针对间接可读对象的概念
      • 5.3.3 迭代器相关概念
      • std::inputoutputiterator<Pos>
      • std::output_iterator<Pos , T\>
      • std::input_iterator<Pos\>
      • std::forward_iterator<Pos\>
      • std::bidirectional_iterator<Pos\>
      • std::randomaccessiterator<Pos\>
      • std::contiguous_iterator<Pos\>
      • std::sentinel_for<S , Pos\>
      • std::sizedsentinelfor<S , Pos\>
      • 5.3.4 算法相关的迭代器概念
    • 5.4 可调用对象相关概念
      • 5.4.1 可调用对象的基本概念
      • 5.4.2 迭代器使用的可调用对象概念
    • 5.5 辅助概念
      • 5.5.1 特定类型属性的概念
      • std::default_initializable<T\>
      • std::move_constructible<T\>
      • std::copy_constructible<T\>
      • std::destructible<T\>
      • std::swappable<T\>
      • 5.5.2 可递增类型的概念
      • std::weakly_incrementable<T\>
      • std::incrementable<T\>
  • 第6章 范围与视图
  • 第7章 范围和视图的实用工具
  • 第8章 视图类型详解
  • 第9章 跨度(Spans)
  • 第10章 格式化输出
  • 第11章 <chrono>中的日期和时区
  • 第12章 std::jthread和停止令牌
  • 第13章 并发特性
  • 第14章 协程
  • 第15章 协程详解
  • 第16章 模块
  • 第17章 Lambda扩展
  • 第18章 编译期计算
  • 第19章 非类型模板参数(NTTP)扩展
  • 第20章 新的类型特性
  • 第21章 核心语言的小改进
  • 第22章 泛型编程的小改进
  • 第23章 C++标准库的小改进
  • 第24章 已弃用和移除的特性
  • cpp20completeguides
zhangxf
2025-03-20
目录

第5章 标准概念详解

# 第5章 标准概念详解

本章详细介绍C++20标准库中的所有概念。

# 5.1 所有标准概念概述

“类型和对象的基本概念”表列出了一般类型和对象的基本概念。“范围、迭代器和算法的概念”表列出了范围、视图、迭代器和算法相关的概念。“辅助概念”表列出了主要用作其他概念构建块的概念,应用程序员通常不会直接使用这些概念。

# 5.1.1 头文件和命名空间

标准概念在不同的头文件中定义:

  • 许多基本概念在头文件<concepts>中定义,<ranges>和<iterator>会包含该头文件。
  • 迭代器相关的概念在头文件<iterator>中定义。
  • 范围相关的概念在头文件<ranges>中定义。
  • three_way_comparable相关概念在<compare>中定义(几乎其他每个头文件都会包含它)。
  • uniform_random_bit_generator在<random>中定义。

几乎所有概念都在命名空间std中定义。唯一的例外是范围相关的概念,它们在命名空间std::ranges中定义。

概念 约束
integral
signed_integral
unsigned_integral
floating_point
整型类型
有符号整型类型
无符号整型类型
浮点型类型
movable
copyable
semiregular
regular
支持移动初始化/赋值和交换
支持移动和复制初始化/赋值以及交换
支持默认初始化、复制、移动和交换
支持默认初始化、复制、移动、交换以及相等性比较
same_as
convertible_to
derived_from
constructible_from
assignable_from
swappable_with
common_with
common_reference_with
相同类型
可转换为另一种类型的类型
从另一种类型派生的类型
可由其他类型构造的类型
可从另一种类型赋值的类型
可与另一种类型交换的类型
两种类型有共同类型
两种类型有共同引用类型
equality_comparable
equality_comparable_with
totally_ordered
totally_ordered_with
three_way_comparable
three_way_comparable_with
类型支持相等性检查
可以检查两种类型是否相等
类型支持严格弱序关系
可以检查两种类型是否满足严格弱序关系
可以应用所有比较运算符(包括<=>运算符)
可以用所有比较运算符(包括<=>)比较两种类型
invocable
regular_invocable
predicate
relation
equivalence_relation
strict_weak_order
uniform_random_bit_generator
类型对于指定参数是可调用的
类型对于指定参数是可调用的(无修改)
类型是一个谓词(可调用且返回布尔值)
可调用类型定义了两种类型之间的关系
可调用类型定义了两种类型之间的相等关系
可调用类型定义了两种类型之间的顺序关系
可调用类型可用作随机数生成器

表5.1 类型和对象的基本概念

概念 约束
default_initializable
move_constructible
copy_constructible
destructible
swappable
weakly_incrementable
incrementable
类型是可默认初始化的
类型支持移动初始化
类型支持复制初始化
类型是可销毁的
类型是可交换的
类型支持递增运算符
类型支持保持相等性的递增运算符

表5.2 辅助概念

概念 约束
range
output_range
input_range
forward_range
bidirectional_range
random_access_range
contiguous_range
sized_range
common_range
borrowed_range
view
viewable_range
类型是一个范围
类型是一个可写入的范围
类型是一个可读的范围
类型是一个可多次读取的范围
类型是一个可向前和向后读取的范围
类型是一个支持在元素间跳跃访问的范围
类型是一个元素在连续内存中的范围
类型是一个支持快速获取大小的范围
类型是一个迭代器和哨兵类型相同的范围
类型是一个左值或借用范围
类型是一个视图
类型是或可转换为一个视图
indirectly_writable
indirectly_readable
indirectly_movable
indirectly_movable_storable
indirectly_copyable
indirectly_copyable_storable
indirectly_swappable
indirectly_comparable
类型可用于写入其引用的位置
类型可用于读取其引用的位置
类型引用的对象是可移动的
类型引用的对象是可移动的且支持临时对象
类型引用的对象是可复制的
类型引用的对象是可复制的且支持临时对象
类型引用的对象是可交换的
类型引用的对象是可比较的
input_output_iterator
output_iterator
input_iterator
forward_iterator
bidirectional_iterator
random_access_iterator
contiguous_iterator
sentinel_for
sized_sentinel_for
类型是一个迭代器
类型是一个输出迭代器
类型是(至少)一个输入迭代器
类型是(至少)一个前向迭代器
类型是(至少)一个双向迭代器
类型是(至少)一个随机访问迭代器
类型是指向连续内存中元素的迭代器
类型可用作某迭代器类型的哨兵
类型可用作某迭代器类型的哨兵且能快速计算距离
permutable
mergeable
sortable
类型是(至少)一个可对元素重新排序的前向迭代器
两种类型可用于将已排序元素合并到第三种类型中
类型是可排序的(根据比较和投影)
indirectly_unary_invocable
indirectly_regular_unary_invocable
indirect_unary_predicate
indirect_binary_predicate
indirect_equivalence_relation
indirect_strict_weak_order
操作可使用迭代器的值类型进行调用
无状态操作可使用迭代器的值类型进行调用
一元谓词可使用迭代器的值类型进行调用
二元谓词可使用两个迭代器的值类型进行调用
谓词可用于检查传递的迭代器的两个值是否相等
谓词可用于对传递的迭代器的两个值进行排序

表5.3 范围、迭代器和算法的概念

# 5.1.2 标准概念的包含关系

C++标准库提供的概念经过精心设计,在合理的情况下会包含其他概念。实际上,它们构建了一个相当复杂的包含关系图。图5.1展示了其复杂程度。

img

图5.1 C++标准概念的包含关系图(节选)

因此,概念的描述中会列出它包含了哪些其他关键概念。

# 5.2 与语言相关的概念

本节列出适用于一般对象和类型的概念。

# 5.2.1 算术概念

# std::integral<T>

  • 保证类型T是整型(包括bool和所有字符类型)。
  • 要求:
    • 类型特性std::is_integral_v<T>为true。

# std::signed_integral<T>

  • 保证类型T是有符号整型(包括有符号字符类型,char可能属于此类)。
  • 要求:
    • 满足std::integral<T>。
    • 类型特性std::is_signed_v<T>为true。

# std::unsigned_integral<T>

  • 保证类型T是无符号整型(包括bool和无符号字符类型,char可能属于此类)。
  • 要求:
    • 满足std::integral<T>。
    • 不满足std::signed_integral<T>。

# std::floating_point<T>

  • 保证类型T是浮点型(float、double或long double)。
  • 引入此概念是为了能够定义数学常量。
  • 要求:
    • 类型特性std::is_floating_point_v<T>为true。

# 5.2.2 对象概念

对于对象(既不是引用、也不是函数、更不是void的类型),存在一系列所需基本操作的层级关系。

# std::movable<T>

  • 保证类型T是可移动且可交换的。也就是说,可以进行移动构造、移动赋值,并且能与同类型的另一个对象进行交换。

  • 要求:

    • 满足std::move_constructible<T>。
    • 满足std::assignable_from<T&, T>。
    • 满足std::swappable<T>。
    • T既不是引用,也不是函数,亦不是void。
  • std::copyable<T>

    • 保证:类型T是可复制的(这意味着它是可移动且可交换的)。
    • 要求:
      • 满足std::movable<T>。
      • 满足std::copy_constructible<T>。
      • 对于任意的T、T&、const T和const T&到T&,满足std::assignable_from。
      • 满足std::swappable<T>。
      • T既不是引用,也不是函数,亦不是void。
  • std::semiregular<T>

    • 保证:类型T是半规则的(可以默认初始化、复制、移动和交换)。
    • 要求:
      • 满足std::copyable<T>。
      • 满足std::default_initializable<T>。
      • 满足std::movable<T>。
      • 满足std::copy_constructible<T>。
      • 对于任意的T、T&、const T和const T&到T&,满足std::assignable_from。
      • 满足std::swappable<T>。
      • T既不是引用,也不是函数,亦不是void。
  • std::regular<T>

    • 保证:类型T是规则的(可以默认初始化、复制、移动、交换,并进行相等性检查)。
    • 要求:
      • 满足std::semiregular<T>。
      • 满足std::equality_comparable<T>。
      • 满足std::copyable<T>。
      • 满足std::default_initializable<T>。
      • 满足std::movable<T>。
      • 满足std::copy_constructible<T>。
      • 对于任意的T、T&、const T和const T&到T&,满足std::assignable_from。
      • 满足std::swappable<T>。
      • T既不是引用,也不是函数,亦不是void。

# 5.2.3 类型间关系的概念

  • std::same_as<T1, T2>
    • 保证:类型T1和T2相同。
    • 该概念两次调用std::is_same_v类型特性,以确保参数顺序无关紧要。
    • 要求:
      • 类型特性std::is_same_v<T1, T2>为真。
      • T1和T2的顺序无关紧要。
  • std::convertible_to<From, To>
    • 保证:类型From的对象既可以隐式转换,也可以显式转换为类型To的对象。
    • 要求:
      • 类型特性std::is_convertible_v<From, To>为真。
      • 支持从From到To的static_cast转换。
      • 可以将类型From的对象作为To返回。
  • std::derived_from<D, B>
    • 保证:类型D是从类型B公开派生的(或者D和B相同),这样任何类型为D的指针都可以转换为类型为B的指针。换句话说,该概念保证D的引用/指针可以当作B的引用/指针使用。
    • 要求:
      • 类型特性std::is_base_of_v<B, D>为真。
      • 类型特性std::is_convertible_v对于类型为D的常量指针到B的转换为真。
  • std::constructible_from<T, Args...>
    • 保证:可以使用类型为Args...的参数初始化类型为T的对象。
    • 要求:
      • 满足std::destructible<T>。
      • 类型特性std::is_constructible_v<T, Args...>为真。
  • std::assignable_from<To, From>
    • 保证:可以将类型From的值移动赋值或复制赋值给类型To的值。赋值操作还必须返回原来的To对象。
    • 要求:
      • To必须是左值引用。
      • 对于类型的常量左值引用,满足std::common_reference_with<To, From>。
      • 必须支持operator =,并且返回类型与To相同。
  • std::swappable_with<T1, T2>
    • 保证:类型T1和T2的值可以交换。
    • 要求:
      • 满足std::common_reference_with<T1, T2>。
      • 任意两个类型为T1和T2的对象可以使用std::ranges::swap()相互交换值。
  • std::common_with<T1, T2>
    • 保证:类型T1和T2共享一个可以显式转换到的公共类型。
    • 要求:
      • 类型特性std::common_type_t<T1, T2>产生一个类型。
      • 支持转换到它们的公共类型的static_cast。
      • 两种类型的引用共享一个common_reference类型。
      • T1和T2的顺序无关紧要。
  • std::common_reference_with<T1, T2>
    • 保证:类型T1和T2共享一个common_reference类型,可以隐式和显式转换到该类型。
    • 要求:
      • 类型特性std::common_reference_t<T1, T2>产生一个类型。
      • 两种类型都可以转换为它们的公共引用类型。
      • T1和T2的顺序无关紧要。

# 5.2.4 比较概念

  • std::equality_comparable<T>
    • 保证:类型T的对象可以使用==和!=运算符进行比较。比较顺序无关紧要。
    • T的operator ==应该是对称且传递的:
      • t1 == t2为真当且仅当t2 == t1为真。
      • 如果t1 == t2和t2 == t3为真,那么t1 == t3为真。然而,这是一个语义约束,无法在编译时检查。
    • 要求:
      • 支持==和!=,并且返回一个可以转换为bool的值。
  • std::equality_comparable_with<T1, T2>
    • 保证:类型T1和T2的对象可以使用==和!=运算符进行比较。
    • 要求:
      • 对于所有涉及T1和 / 或T2对象的比较,都支持==和!=,并且返回相同类型且可转换为bool的值。
  • std::totally_ordered<T>
    • 保证:类型T的对象可以使用==、!=、<、<=、>和>=运算符进行比较,这样两个值总是相等、小于或大于彼此。
    • 该概念并不要求类型T对所有值都有全序关系。实际上,即使浮点值没有全序关系,表达式std::totally_ordered<double>也会返回true:
std::totally_ordered<double>                                           // true
std::totally_ordered<std::pair<double, double>>     // true
std::totally_ordered<std::complex<int>>                     // false
1
2
3

因此,这个概念用于检查进行元素排序所需的形式要求。它被std::ranges::less使用,std::ranges::less是排序算法的默认排序准则。这样,如果类型对所有值没有全序关系,排序算法也不会编译失败。支持所有六个基本比较运算符就足够了。要排序的值应该具有全序或弱序关系。然而,这是一个语义约束,无法在编译时检查。 - 要求: - 满足std::equality_comparable<T>。 - 所有使用==、!=、<、<=、>和>=运算符的比较都返回一个可以转换为bool的值。

  • std::totally_ordered_with<T1, T2>
    • 保证:类型T1和T2的对象可以使用==、!=、<、<=、>和>=运算符进行比较,这样两个值总是相等、小于或大于彼此。
    • 要求:
      • 对于所有涉及T1和 / 或T2对象的比较,都支持==、!=、<、<=、>和>=,并且返回相同类型且可转换为bool的值。
  • std::three_way_comparable<T>、std::three_way_comparable<T, Cat>
    • 保证:类型T的对象可以使用==、!=、<、<=、>、>=和<=>运算符进行比较(并且至少具有比较类别类型Cat)。如果没有传递Cat,则要求为std::partial_ordering。
    • 这个概念在头文件<compare>中定义。
    • 注意,这个概念并不意味着也不涵盖std::equality_comparable,因为后者要求只有当两个对象相等时operator ==才返回true。在弱序或偏序关系中,情况可能并非如此。
    • 注意,这个概念并不意味着也不涵盖std::totally_ordered,因为后者要求比较类别是std::strong_ordering或std::weak_ordering。
    • 要求:
      • 所有使用==、!=、<、<=、>和>=运算符的比较都返回一个可以转换为bool的值。
      • 任何使用<=>运算符的比较都返回一个比较类别(至少是Cat)。
  • std::three_way_comparable_with<T1, T2>、std::three_way_comparable_with<T1, T2, Cat>
    • 保证:任意两个类型为T1和T2的对象可以使用==、!=、<、<=、>、>=和<=>运算符进行比较(并且至少具有比较类别类型Cat)。如果没有传递Cat,则要求为std::partial_ordering。
    • 这个概念在头文件<compare>中定义。
    • 注意,这个概念并不意味着也不涵盖std::equality_comparable_with,因为后者要求只有当两个对象相等时operator ==才返回true。在弱序或偏序关系中,情况可能并非如此。
    • 注意,这个概念并不意味着也不涵盖std::totally_ordered_with,因为后者要求比较类别是std::strong_ordering或std::weak_ordering。
    • 要求:
      • 对于T1和T2(以及Cat)的值和公共引用,满足std::three_way_comparable。
      • 所有使用==、!=、<、<=、>和>=运算符的比较都返回一个可以转换为bool的值。
      • 任何使用<=>运算符的比较都返回一个比较类别(至少是Cat)。
      • T1和T2的顺序无关紧要。

# 5.3 迭代器和范围的概念

本节列出了迭代器和范围的所有基本概念,这些概念在算法和类似函数中很有用。

注意,范围相关的概念是在命名空间std::ranges中提供的,而不是在std命名空间中。它们在头文件<ranges>中声明。

迭代器相关的概念在头文件<iterator>中声明。

# 5.3.1 范围和视图的概念

定义了几个概念来约束范围。它们与迭代器的概念相对应,并且考虑到自C++20以来有了新的迭代器类别。

  • std::ranges::range<Rg>

    • 保证:Rg是一个有效的范围。
    • 这意味着类型为Rg的对象支持通过使用std::ranges::begin()和std::ranges::end()对元素进行迭代。如果范围是数组,或者提供begin()和end()成员函数,又或者可以与独立的begin()和end()函数一起使用,就满足这个条件。
  • 此外,对于std::ranges::begin()和std::ranges::end(),有以下约束:

    • 它们必须在(均摊)常数时间内操作。
    • 它们不会修改范围。
    • 除非范围至少没有提供前向迭代器,否则多次调用begin()会得到相同的位置。

这意味着我们可以高效地遍历所有元素(除非只有纯输入迭代器,否则甚至可以多次遍历)。

  • 要求:
    • 对于类型为Rg的对象rg,支持std::ranges::begin(rg)且支持std::ranges::end(rg)。
    • std::ranges::output_range<Rg, T>
      • 保证Rg是一个范围,它至少提供输出迭代器(可用于写入的迭代器),这些迭代器可以接受类型为T的值。
      • 要求:
        • std::range<Rg>满足。
        • 迭代器类型和T满足std::output_iterator。
    • std::ranges::input_range<Rg>
      • 保证Rg是一个范围,它至少提供输入迭代器(可用于读取的迭代器)。
      • 要求:
        • std::range<Rg>满足。
        • 迭代器类型满足std::input_iterator。
    • std::ranges::forward_range<Rg>
      • 保证Rg是一个范围,它至少提供前向迭代器(可用于读取、写入和多次遍历的迭代器)。
      • 注意,迭代器的iterator_category成员可能不匹配。对于生成纯右值(prvalue)的迭代器,如果可用,其iterator_category为std::input_iterator_tag。
      • 要求:
        • std::input_range<Rg>满足。
        • 迭代器类型满足std::forward_iterator。
    • std::ranges::bidirectional_range<Rg>
      • 保证Rg是一个范围,它至少提供双向迭代器(可用于读取、写入以及反向遍历的迭代器)。
      • 注意,迭代器的iterator_category成员可能不匹配。对于生成纯右值的迭代器,如果可用,其iterator_category为std::input_iterator_tag。
      • 要求:
        • std::forward_range<Rg>满足。
        • 迭代器类型满足std::bidirectional_iterator。
    • std::ranges::random_access_range<Rg>
      • 保证Rg是一个范围,它提供随机访问迭代器(可用于读取、写入、前后跳转以及计算距离的迭代器)。
      • 注意,迭代器的iterator_category成员可能不匹配。对于生成纯右值的迭代器,如果可用,其iterator_category为std::input_iterator_tag。
      • 要求:
        • std::bidirectional_range<Rg>满足。
        • 迭代器类型满足std::random_access_iterator。
    • std::ranges::contiguous_range<Rg>
      • 保证Rg是一个范围,它提供随机访问迭代器,并且额外要求元素存储在连续内存中。
      • 注意,迭代器的iterator_category成员不匹配。如果定义了该类别成员,它只能是std::random_access_iterator_tag,如果值是纯右值,甚至只能是std::input_iterator_tag。
      • 要求:
        • std::random_access_range<Rg>满足。
        • 迭代器类型满足std::contiguous_iterator。
        • 调用std::ranges::data()会返回指向第一个元素的原始指针。
    • std::ranges::sized_range<Rg>
      • 保证Rg是一个范围,其中元素的数量可以在常数时间内计算出来(可以通过调用成员函数size(),或者计算起始和结束位置的差值)。
      • 如果满足这个概念,对于类型为Rg的对象,std::ranges::size()是快速且定义明确的。
      • 注意,这个概念的性能方面是一个语义约束,在编译时无法检查。如果一个类型虽然提供了size(),但并不满足这个概念,可以定义std::disable_sized_range<Rg>为true来表明这一点。
      • 要求:
        • std::range<Rg>满足。
        • 支持调用std::ranges::size()。
    • std::ranges::common_range<Rg>
      • 保证Rg是一个范围,其中起始迭代器和哨兵(结束迭代器)具有相同的类型。
      • 以下情况总是满足这个保证:
        • 所有标准容器(vector、list等)。
        • empty_view。
        • single_view。
        • common_view。
      • 以下情况不满足这个保证:
        • take views。
        • const drop views。
        • 没有结束值或结束值类型不同的iota views。
        • 对于其他视图,这取决于底层范围的类型。
      • 要求:
        • std::range<Rg>满足。
        • std::ranges::iterator_t<Rg>和std::ranges::sentinel_t<Rg>具有相同的类型。
    • std::ranges::borrowed_range<Rg>
      • 保证在当前上下文中传递的范围Rg生成的迭代器,即使在范围不再存在时也能使用。如果传递的是左值,或者传递的范围始终是借用范围,那么这个概念就满足。
      • 如果满足这个概念,范围的迭代器就不依赖于范围的生命周期。这意味着当创建迭代器的范围被销毁时,迭代器不会悬空。但是,如果范围的迭代器引用了底层范围,而底层范围不再存在,迭代器仍然可能悬空。
      • 形式上,如果Rg是一个左值(例如有名称的对象),或者变量模板std::ranges::enable_borrowed_range<Rg>为true,则满足这个概念,以下视图属于这种情况:subrange、ref_view、string_view、span、iota_view和empty_view。
      • 要求:
        • std::range<Rg>满足。
        • Rg是左值或者enable_borrowed_range<Rg>为true。
    • std::ranges::view<Rg>
      • 保证Rg是一个视图(一个复制、移动、赋值和销毁成本都很低的范围)。
      • 一个视图有以下要求:
        • 它必须是一个范围(支持遍历元素)。
        • 它必须是可移动的。
        • 移动构造函数,如果有复制构造函数的话也包括复制构造函数,必须具有常数复杂度。
        • 移动赋值运算符,如果有复制赋值运算符的话也包括复制赋值运算符,必须成本很低(常数复杂度或者不比销毁加创建的成本更差)。
        • 除了最后一个要求,其他要求都由相应的概念检查。最后一个要求是一个语义约束,类型的实现者必须通过从std::ranges::view_interface公开派生,或者特化std::ranges::enable_view<Rg>为true来保证。
      • 要求:
        • std::range<Rg>满足。
        • std::movable<Rg>满足。
        • 变量模板std::ranges::enable_view<Rg>为true。
    • std::ranges::viewable_range<Rg>
      • 保证Rg是一个范围,可以使用std::views::all()适配器安全地转换为视图。
      • 如果Rg已经是一个视图,或者是范围的左值,或者是可移动的右值范围,但不是初始化列表,那么这个概念就满足。
      • 要求:
        • std::range<Rg>满足。

# 5.3.2 针对类似指针对象的概念

本节列出了所有标准概念,这些概念用于那些可以使用*运算符来处理其所指向值的对象。这通常适用于原始指针、智能指针和迭代器。因此,这些概念通常用作处理迭代器和算法的概念的基本约束。

请注意,这些概念并不要求支持->运算符。这些概念在头文件<iterator>中声明。

# 间接支持的基本概念

  • std::indirectly_writable<P, Val>
    • 保证P是一个类似指针的对象,支持使用*运算符来赋值Val。
    • 非const原始指针、智能指针以及迭代器(前提是Val可以赋值给P所指向的位置)满足这个概念。
  • std::indirectly_readable<P>
    • 保证P是一个类似指针的对象,支持使用*运算符进行读取访问。
    • 原始指针、智能指针和迭代器满足这个概念。
    • 要求:
      • 对于const和non-const对象,结果值必须具有相同的引用类型(这排除了std::optional<>)。这确保了P的常量性不会传播到它所指向的对象(当运算符返回对成员的引用时,通常不是这种情况)。
      • std::iter_value_t<P>必须有效。该类型不必支持->运算符。

# 针对间接可读对象的概念

对于间接可读的类似指针概念,你可以检查其他约束:

  • std::indirectly_movable<InP, OutP>
    • 保证InP的值可以直接移动赋值给OutP的值。
    • 有了这个概念,以下代码是有效的:
void foo(InP inPos, OutP outPos) {
    *outPos = std::move(*inPos);
}
1
2
3
- **要求**:
    - `std::indirectly_readable<InP>`满足。
    - `InP`值的右值引用对于(`OutP`的值)满足`std::indirectly_writable`。
  • std::indirectly_movable_storable<InP, OutP>
    • 保证即使使用OutP所指向类型的(临时)对象,InP的值也可以间接移动赋值给OutP的值。
    • 有了这个概念,以下代码是有效的:
void foo(InP inPos, OutP outPos) {
    OutP::value_type tmp = std::move(*inPos);
    *outPos = std::move(tmp);
}
1
2
3
4
- **要求**:
    - `std::indirectly_movable<InP, OutP>`满足。
    - `InP`值对于`OutP`所指向的对象满足`std::indirectly_writable`。
    - `InP`所指向的值满足`std::movable`。
    - `InP`所指向的右值是可复制/移动构造和可赋值的。
  • std::indirectly_copyable<InP, OutP>
    • 保证InP的值可以直接赋值给OutP的值。
    • 有了这个概念,以下代码是有效的:
void foo(InP inPos, OutP outPos) {
    *outPos = *inPos;
}
1
2
3
- **要求**:
    - `std::indirectly_readable<InP>`满足。
    - `InP`值的引用对于(`OutP`的值)满足`std::indirectly_writable`。
  • std::indirectly_copyable_storable<InP, OutP>
    • 保证即使使用OutP所指向类型的(临时)对象,InP的值也可以间接赋值给OutP的值。
    • 有了这个概念,以下代码是有效的:
void foo(InP inPos, OutP outPos) {
    OutP::value_type tmp = *inPos;
    *outPos = tmp;
}
1
2
3
4
- **要求**:
    - `std::indirectly_copyable<InP, OutP>`满足。
    - `InP`值的const左值和右值引用对于`OutP`所指向的对象满足`std::indirectly_writable`。
    - `InP`所指向的值满足`std::copyable`。
    - `InP`所指向的值是可复制/移动构造和可赋值的。
  • std::indirectly_swappable<P>
  • std::indirectly_swappable<P1, P2>
    • 保证P或P1和P2的值可以交换(使用std::ranges::iter_swap())。
    • 要求:
      • std::indirectly_readable<P1>(以及std::indirectly_readable<P2>)满足。
      • 对于任意两个类型为P1和P2的对象,支持std::ranges::iter_swap()。
  • std::indirectly_comparable<P1, P2, Comp>
  • std::indirectly_comparable<P1, P2, Comp, Proj1>
  • std::indirectly_comparable<P1, P2, Comp, Proj1, Proj2>
    • 保证可以比较P1和P2所指向的元素(可选地使用Proj1和Proj2进行转换)。
    • 要求:
      • Comp满足std::indirect_binary_predicate。
      • std::projected<P1, Proj1>和std::projected<P2, Proj2>满足,默认投影为std::identity。

# 5.3.3 迭代器相关概念

本节列出了用于要求不同类型迭代器的概念。自C++20起,我们有了新的迭代器分类,这些概念就涉及到了这一点,并且它们与范围相关概念相对应。这些概念在头文件<iterator>中提供。

# std::input_output_iterator<Pos>

  • 保证Pos支持所有迭代器的基本接口:operator++和operator*,其中operator*必须引用一个值。该概念并不要求迭代器是可复制的(因此,这比算法使用的迭代器的基本要求更低)。
  • 要求:
    • 满足std::weakly_incrementable<pos>。
    • operator *返回一个引用。

# std::output_iterator<Pos , T>

  • 保证Pos是一个输出迭代器(可以向其指向的元素赋值的迭代器),可以将类型为T的值赋给它所指向的值。
  • 类型为Pos的迭代器可用于通过*i++ = val;来为类型为T的值val赋值。
  • 这些迭代器仅适用于单遍迭代。
  • 要求:
    • 满足std::input_or_output_iterator<Pos>。
    • 满足std::indirectly_writable<Pos, I>。

# std::input_iterator<Pos>

  • 保证Pos是一个输入迭代器(可以从元素中读取值的迭代器)。
  • 如果不满足std::forward_iterator,这些迭代器仅适用于单遍迭代。
  • 要求:
    • 满足std::input_or_output_iterator<Pos>。
    • 满足std::indirectly_readable<Pos>。
    • Pos的迭代器类别派生自std::input_iterator_tag。

# std::forward_iterator<Pos>

  • 保证Pos是一个前向迭代器(可以对元素进行多次正向迭代的读取迭代器)。
  • 注意,迭代器的iterator_category成员可能不匹配。对于产生纯右值(prvalue)的迭代器,它是std::input_iterator_tag(如果可用)。
  • 要求:
    • 满足std::input_iterator<Pos>。
    • 满足std::incrementable<Pos>。
    • Pos的迭代器类别派生自std::forward_iterator_tag。

# std::bidirectional_iterator<Pos>

  • 保证Pos是一个双向迭代器(可以对元素进行多次正向和反向迭代的读取迭代器)。
  • 注意,迭代器的iterator_category成员可能不匹配。对于产生纯右值的迭代器,它是std::input_iterator_tag(如果可用)。
  • 要求:
    • 满足std::forward_iterator<Pos>。
    • 支持使用operator --进行反向迭代。
    • Pos的迭代器类别派生自std::bidirectional_iterator_tag。

# std::random_access_iterator<Pos>

  • 保证Pos是一个随机访问迭代器(可以在元素间前后跳跃的读取迭代器)。
  • 注意,迭代器的iterator_category成员可能不匹配。对于产生纯右值的迭代器,它是std::input_iterator_tag(如果可用)。
  • 要求:
    • 满足std::bidirectional_iterator<Pos>。
    • 满足std::totally_ordered<Pos>。
    • 满足std::sized_sentinel_for<Pos, Pos>。
    • 支持+、+=、-、-=、[]操作。
    • Pos的迭代器类别派生自std::random_access_iterator_tag。

# std::contiguous_iterator<Pos>

  • 保证Pos是一个迭代器,用于迭代连续内存中的元素。
  • 注意,迭代器的iterator_category成员不匹配。如果定义了该类别成员,它只是std::random_access_iterator_tag,如果值是纯右值,甚至只是std::input_iterator_tag。
  • 要求:
    • 满足std::random_access_iterator<Pos>。
    • Pos的迭代器类别派生自std::contiguous_iterator_tag。
    • 对一个元素调用to_address()会返回指向该元素的原始指针。

# std::sentinel_for<S , Pos>

  • 保证S可以用作Pos的哨兵(可能是不同类型的结束迭代器)。
  • 要求:
    • 满足std::semiregular<S>。
    • 满足std::input_or_output_iterator<Pos>。
    • 可以使用==和!=操作符比较Pos和S。

# std::sized_sentinel_for<S , Pos>

  • 保证S可以用作Pos的哨兵(可能是不同类型的结束迭代器),并且可以在常量时间内计算它们之间的距离。
  • 若要表明无法在常量时间内计算距离(即使可以计算距离),可以定义std::disable_sized_sentinel_for<Rg>产生true。
  • 要求:
    • 满足std::sentinel_for<S, Pos>。
    • 对Pos和S调用operator -会产生迭代器差值类型的值。
    • 没有定义std::disable_sized_sentinel_for<S , Pos>产生true。

# 5.3.4 算法相关的迭代器概念

std::permutable<Pos>

  • 保证可以使用operator ++正向迭代,并通过移动和交换操作重新排列元素。
  • 要求:
    • 满足std::forward_iterator<Pos>。
    • 满足std::indirectly_movable_storable<Pos>。
    • 满足std::indirectly_swappable<Pos>。

std::mergeable<Pos1 , Pos2 , ToPos>

std::mergeable<Pos1 , Pos2 , ToPos , Comp>

std::mergeable<Pos1 , Pos2 , ToPos , Comp , Proj1>

std::mergeable<Pos1 , Pos2 , ToPos , Comp , Proj1 , Proj2>

  • 保证可以将两个已排序序列中Pos1和Pos2指向的元素,通过复制合并到ToPos指向的序列中。顺序由operator <或Comp(应用于可选地通过投影Proj1和Proj2转换后的值)定义。
  • 要求:
    • Pos1和Pos2都满足std::input_iterator。
    • 满足std::weakly_incrementable<ToPos>。
    • Pos1和Pos2都满足std::indirectly_copyable<PosN, ToPos>。
    • 满足std::indirect_strict_weak_order对Comp以及std::projected<Pos1, Proj1>和std::projected<Pos2, Proj2>的要求(默认比较为<,默认投影为std::identity),这意味着:
      • Pos1和Pos2都满足std::indirectly_readable。
      • 满足std::copy_constructible<Comp>。
      • 对于Comp&以及(投影后的)值/引用类型,满足std::strict_weak_order<Comp>。

std::sortable<Pos>

std::sortable<Pos , Comp>

std::sortable<Pos , Comp , Proj>

  • 保证可以使用operator <或Comp(在对值可选地应用投影Proj之后)对迭代器Pos指向的元素进行排序。
  • 要求:
    • 满足std::permutable<Pos>。
    • 满足std::indirect_strict_weak_order对Comp以及(投影后的)值的要求(默认比较为<,默认投影为std::identity),这意味着:
      • 满足std::indirectly_readable<Pos>。
      • 满足std::copy_constructible<Comp>。
      • 对于Comp&以及(投影后的)值/引用类型,满足std::strict_weak_order<Comp>。

# 5.4 可调用对象相关概念

本节列出了所有与可调用对象相关的概念,包括:

  • 函数
  • 函数对象
  • lambda表达式
  • 成员指针(将对象的类型作为额外参数)

# 5.4.1 可调用对象的基本概念

std::invocable<Op , ArgTypes...>

  • 保证可以使用类型为ArgTypes...的参数调用Op。
  • Op可以是函数、函数对象、lambda表达式或成员指针。
  • 若要表明操作和传递的参数都不会被修改,可以使用std::regular_invocable。不过,请注意这两个概念之间只有语义上的差异,无法在编译时进行检查。因此,概念上的差异仅用于说明意图。
  • 例如:
struct  S  {
    int member;
    int mfunc(int);
    void  operator()(int  i)  const ; 
};

void  testCallable() {
    std::invocable<decltype(testCallable)>           // 满足
    std::invocable<decltype([](int){}),  char>     // 满足(char可转换为int)
    std::invocable<decltype(&S::mfunc),  S,  int> // 满足(成员指针、对象、参数)
    std::invocable<decltype(&S::member),  S>;       // 满足(成员指针和对象)
    std::invocable<S,  int>;                                         // 由于operator()而满足
}
1
2
3
4
5
6
7
8
9
10
11
12
13

注意,作为类型约束,只需要指定参数类型:

void  callWithIntAndString(std::invocable<int,  std::string>  auto  op);
1

有关完整示例,请参阅将lambda表达式用作非类型模板参数的用法。

  • 要求:
    • 对于类型为Op的op和类型为ArgTypes...的args,std::invoke(op, args)有效。

std::regular_invocable<Op , ArgTypes...>

  • 保证可以使用类型为ArgTypes...的参数调用Op,并且调用不会改变传递的操作状态和传递的参数状态。
  • Op可以是函数、函数对象、lambda表达式或成员指针。
  • 注意,与std::invocable概念的差异纯粹是语义上的,无法在编译时进行检查。因此,概念上的差异仅用于说明意图。
  • 要求:
    • 对于类型为Op的op和类型为ArgTypes...的args,std::invoke(op, args)有效。

std::predicate<Op , ArgTypes...>

  • 保证可调用对象(函数、函数对象、lambda表达式)Op是一个谓词,可以使用类型为ArgTypes...的参数进行调用。
  • 这意味着调用不会改变传递的操作状态和传递的参数状态。
  • 要求:
    • 满足std::regular_invocable<Op>。
    • 所有使用ArgTypes...调用Op的操作都产生一个可以用作布尔值的值。

std::relation<Pred , T1 , T2>

  • 保证任何两个类型为T1和T2的对象之间存在二元关系,即它们可以作为参数传递给二元谓词Pred。
  • 这意味着调用不会改变传递的操作状态和传递的参数状态。
  • 注意,与std::equivalence_relation和std::strict_weak_order概念的差异纯粹是语义上的,无法在编译时进行检查。因此,概念上的差异仅用于说明意图。
  • 要求:
    • 对于pred以及任何T1和T2类型的对象组合,满足std::predicate。

std::equivalence_relation<Pred , T1 , T2>

  • 保证当使用Pred进行比较时,任何两个类型为T1和T2的对象之间存在等价关系。也就是说,它们可以作为参数传递给二元谓词Pred,并且该关系是自反的、对称的和传递的。
  • 这意味着调用不会改变传递的操作状态和传递的参数状态。
  • 注意,与std::relation和std::strict_weak_order概念的差异纯粹是语义上的,无法在编译时进行检查。因此,概念上的差异仅用于说明意图。
  • 要求:
    • 对于pred以及任何T1和T2类型的对象组合,满足std::predicate。

std::strict_weak_order<Pred , T1 , T2>

  • 保证当使用Pred进行比较时,任何两个类型为T1和T2的对象之间存在严格弱序关系。也就是说,它们可以作为参数传递给二元谓词Pred,并且该关系是反自反的和传递的。
  • 这意味着调用不会改变传递的操作状态和传递的参数状态。
  • 注意,与std::relation和std::equivalence_relation概念的差异纯粹是语义上的,无法在编译时进行检查。因此,概念上的差异仅用于说明意图。
  • 要求:
    • 对于pred以及任何T1和T2类型的对象组合,满足std::predicate。

std::uniform_random_bit_generator<Op>

  • 保证Op可以用作随机数生成器,理想情况下,它返回的无符号整数值具有相等的概率。
  • 这个概念在头文件<random>中定义。
  • 要求:
    • 满足std::invocable<Op&>。
    • 调用的结果满足std::unsigned_integral。
    • 支持表达式Op::min()和Op::max(),并且它们产生与生成器调用相同的类型。
    • Op::min()小于Op::max()。

# 5.4.2 迭代器使用的可调用对象概念

std::indirectly_unary_invocable<Op, Pos>

  • 保证Op可以使用Pos所引用的值进行调用。
  • 注意,与indirectly_regular_unary_invocable概念的区别纯粹是语义上的,无法在编译时进行检查。因此,这些不同的概念仅用于记录意图。
  • 要求:
    • 满足std::indirectly_readable<Pos>。
    • 满足std::copy_constructible<Op>。
    • Op&以及Pos的值类型和(公共)引用类型满足std::invocable。
    • 使用值和引用调用Op的结果具有公共引用类型。

std::indirectly_regular_unary_invocable<Op, Pos>

  • 保证Op可以使用Pos所引用的值进行调用,并且调用不会改变Op的状态。
  • 注意,与std::indirectly_unary_invocable概念的区别纯粹是语义上的,无法在编译时进行检查。因此,这些不同的概念仅用于记录意图。
  • 要求:
    • 满足std::indirectly_readable<Pos>。
    • 满足std::copy_constructible<Op>。
    • Op&以及Pos的值类型和(公共)引用类型满足std::regular_invocable。
    • 使用值和引用调用Op的结果具有公共引用类型。

std::indirect_unary_predicate<Pred, Pos>

  • 保证一元谓词Pred可以使用Pos所引用的值进行调用。
  • 要求:
    • 满足std::indirectly_readable<Pos>。
    • 满足std::copy_constructible<Pred>。
    • Pred&以及Pos的值类型和(公共)引用类型满足std::predicate。
    • 所有对Pred的调用都产生一个可以用作布尔值的值。

std::indirect_binary_predicate<Pred, Pos1, Pos2>

  • 保证二元谓词Pred可以使用Pos1和Pos2所引用的值进行调用。
  • 要求:
    • Pos1和Pos2都满足std::indirectly_readable。
    • 满足std::copy_constructible<Pred>。
    • Pred&、Pos1的值类型或(公共)引用类型以及Pos2的值类型或(公共)引用类型满足std::predicate。
    • 所有对Pred的调用都产生一个可以用作布尔值的值。

std::indirect_equivalence_relation<Pred, Pos1>、std::indirect_equivalence_relation<Pred, Pos1, Pos2>

  • 保证二元谓词Pred可以被调用,以检查Pos1中的两个值或Pos1/Pos2中的两个值是否等价。
  • 注意,与std::indirectly_strict_weak_order概念的区别纯粹是语义上的,无法在编译时进行检查。因此,这些不同的概念仅用于记录意图。
  • 要求:
    • Pos1和Pos2都满足std::indirectly_readable。
    • 满足std::copy_constructible<Pred>。
    • Pred&、Pos1的值类型或(公共)引用类型以及Pos2的值类型或(公共)引用类型满足std::equivalence_relation。
    • 所有对Pred的调用都产生一个可以用作布尔值的值。

std::indirect_strict_weak_order<Pred, Pos1>、std::indirect_strict_weak_order<Pred, Pos1, Pos2>

  • 保证二元谓词Pred可以被调用,以检查Pos1中的两个值或Pos1/Pos2中的两个值是否具有严格弱序关系。
  • 注意,与std::indirectly_equivalence_relation概念的区别纯粹是语义上的,无法在编译时进行检查。因此,这些不同的概念仅用于记录意图。
  • 要求:
    • Pos1和Pos2都满足std::indirectly_readable。
    • 满足std::copy_constructible<Pred>。
    • Pred&、Pos1的值类型或(公共)引用类型以及Pos2的值类型或(公共)引用类型满足std::strict_weak_order。
    • 所有对Pred的调用都产生一个可以用作布尔值的值。

# 5.5 辅助概念

本节描述了一些标准化的概念,这些概念主要用于实现其他概念。在应用代码中,你通常不应使用它们。

# 5.5.1 特定类型属性的概念

# std::default_initializable<T>

  • 保证类型T支持默认构造(声明/构造时无需初始值)。
  • 要求:
    • 满足std::constructible_from<T>。
    • 满足std::destructible<T>。
    • T{}有效。
    • T x;有效。

# std::move_constructible<T>

  • 保证T类型的对象可以用其类型的右值进行初始化。
  • 这意味着以下操作是有效的(尽管可能是复制而非移动):T t2{std::move(t1)} (对于任何T类型的t1)。之后,t2应具有t1之前的值,这是一个无法在编译时检查的语义约束。
  • 要求:
    • 满足std::constructible_from<T, T>。
    • 满足std::convertible<T, T>。
    • 满足std::destructible<T>。

# std::copy_constructible<T>

  • 保证T类型的对象可以用其类型的左值进行初始化。
  • 这意味着以下操作是有效的:T t2{t1} (对于任何T类型的t1)。之后,t2应等于t1,这是一个无法在编译时检查的语义约束。
  • 要求:
    • 满足std::move_constructible<T>。
    • std::constructible_from和std::convertible_to对于任何T、T&、const T和const T&到T的转换都满足。
    • 满足std::destructible<T>。

# std::destructible<T>

  • 保证T类型的对象在被销毁时不会抛出异常。 注意,即使是已实现的析构函数,除非你显式用noexcept(false)标记,否则也会自动保证不抛出异常。
  • 要求:
    • 类型特性std::is_nothrow_destructible_v<T>的值为true。

# std::swappable<T>

  • 保证可以交换两个T类型对象的值。
  • 要求:
    • 可以对两个T类型的对象调用std::ranges::swap()。

# 5.5.2 可递增类型的概念

# std::weakly_incrementable<T>

  • 保证类型T支持递增运算符。
  • 注意,这个概念并不要求对同一值进行两次递增的结果相等。因此,这仅适用于单遍迭代的要求。
  • 与std::incrementable相比,以下情况该概念也满足:
    • 类型不可默认构造、不可复制或不可比较相等。
    • 后缀递增运算符返回void(或任何其他类型)。
    • 对同一值进行两次递增得到不同的结果。
  • 注意,与std::incrementable概念的差异纯粹是语义上的,因此递增结果不同的类型在技术上仍可能满足incrementable概念。为了针对这种语义差异实现不同的行为,你应该使用迭代器概念。
  • 要求:
    • 满足std::default_initializable<T>。
    • 满足std::movable<T>。
    • std::iter_difference_t<T>是一个有效的有符号整数类型。

# std::incrementable<T>

  • 保证类型T是一个可递增类型,这样你就可以对相同的值序列进行多次迭代。
  • 与std::weakly_incrementable相比,该概念要求:
    • 对同一值进行两次递增的结果相同(就像前向迭代器那样)。
    • 类型T可默认构造、可复制且可比较相等。
    • 后缀递增运算符返回迭代器的副本(返回类型为T)。
  • 注意,与std::weakly_incrementable概念的差异纯粹是语义上的,因此递增结果不同的类型在技术上仍可能满足incrementable概念。为了针对这种语义差异实现不同的行为,你应该使用迭代器概念。
  • 要求:
    • 满足std::weakly_incrementable<T>。
    • 满足std::regular<T>,这样类型就可默认构造、可复制且可比较相等。
上次更新: 2025/03/20, 19:44:38
第4章 概念、需求和约束详解
第6章 范围与视图

← 第4章 概念、需求和约束详解 第6章 范围与视图→

最近更新
01
C++语言面试问题集锦 目录与说明
03-27
02
第四章 Lambda函数
03-27
03
第二章 关键字static及其不同用法
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式