《STL源码分析》学习笔记 — C++20 concepts
在C++20之前的版本,迭代器相关类型的获取都是通过我们前面学习过的 iterator_traits。这个结构比较好理解。在C++20及之后,C++中提供了两个新的关键字 concept 和 requires 用于定义概念。
一、什么是concepts
在C++参考手册中,是这样描述概念的:
Class templates, function templates, and non-template functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.
Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint
类模板、函数模板、非模板方法(特指类模板中的方法)可能会与某个约束相关联,用以特化对模板参数的要求。这可以被用来选择最合适的函数重载和模板特化。
这样的具名要求集合被称为概念。每个概念是一个断言,在编译时评估,而且该概念作为限制使用的模板的一部分。因此 concept 的违反将在编译时被检测到:
std::list<int> l = {
3,-1,10};
std::sort(l.begin(), l.end());
//Typical compiler diagnostic without concepts:
// invalid operands to binary expression ('std::_List_iterator<int>' and
// 'std::_List_iterator<int>')
// std::__lg(__last - __first) * 2);
// ~~~~~~ ^ ~~~~~~~
// ... 50 lines of output ...
//
//Typical compiler diagnostic with concepts:
// error: cannot call std::sort with std::_List_iterator<int>
// note: concept RandomAccessIterator<std::_List_iterator<int>> was not satisfied
上述代码中 sort 算法需要的迭代器为随机访问迭代器,因此编译无法通过。
二、concepts 的定义
使用 concept 关键字的语法为:
template < template-parameter-list >
concept concept-name = constraint-expression;
如:
template <class T, class U>
concept Derived = std::is_base_of<U, T>::value;
concept 不能递归依赖自身,其模板参数也不能使用约束。除此之外,concept 的显式实例化、显式特化、部分特化都是不支持的。
concept 可以被用于 id 表达式中。当其限制被满足时,表达式为真;否则, 表达式为假:
#include <iostream>
template<typename T>
concept dereferenceable = requires (T a)
{
*a;
};
int main()
{
constexpr bool bl1 = dereferenceable<int>;
std::cout << bl1 << std::endl;
constexpr bool bl2 = dereferenceable<int*>;
std::cout << bl2 << std::endl;
}
concept 还能被用于类型限制中,作为类型模板参数声明、占位类型说明符 以及复合要求中。其中,concept 被用于占位类型说明符主要是用于约束推导的类型:
#include <iostream>
template<typename T>
concept dereferenceable = requires (T a)
{
*a;
};
int main()
{
int a = 1;
auto b = a;
dereferenceable auto c = a; //invalid,int 不满足 dereferenceable
dereferenceable auto d = &a;
}
三、constraints
一个约束是一系列特化了模板参数要求的操作及操作数。它们可以直接出现在 requires 表达式中并且直接作为 concept 的一部分。
1、Conjunctions
我们可以使用 && 连接两个约束,