CTTCG 附录 E Concept
定义和使用 concept
下面演示了几种 C++20 支持的定义 concept 的方式,每一条约束都是以下之一,然后用分号结尾:
- 类型
- 表达式
- { 表达式 } noexcept -> 检查返回类型是否满足其他约束(这个 noexcept 可以不要)
- 引入其他
requires
表达式
template <typename T>
concept StringConcept = requires(T t) {
typename T::iterator; // 1. 检查类型存在性
t.data(); // 2. 检查表达式合法性
// 3. 将表达式作为 concept 的第一个参数,要求 concept 成立(额外参数从第二个参数起放置)
{ t.c_str() } -> std::same_as<char const *>;
// 4. 用 requires 语句引入子条件
requires std::is_pointer_v<decltype(t.data())>;
// 5. 除了 requires 体之外还能用其他编译期常量表达式做约束
} && (sizeof(T) > 8) && !std::is_aggregate_v<T>;
template<StringConcept Str>
void takeString(Str const &s) { }
int main() {
takeString(std::string{"hello"});
}
在模板中加上 requires
条件则相对比较简单。只需要在 模板参数后,函数返回值前面,或者 函数体前面 加上 requires
约束。requires
约束能使用一般的条件表达式,也能把 concept 当成 type trait 使用。比如:
template <typename T>
requires HasPlus<T>
int f(T p)
{}
template <typename T>
int g(T p)
requires HasPlus<T>
{}
// 注意 requires 约束的位置比较灵活
上方代码中 template<StringConcept Str>
是对 template<typename Str> requires StringConcept<Str>
的缩写。只有单参数 concept 可以用这种简写方法。(是的,concept 可以有多个参数!)
requires
表达式可以作为函数 requires 要求的一部分,形式上有两个连着的 requires
关键字,但是它们的含义不同:
template<typename T>
void f(T) requires requires(T t) { t + t; }
{}
concept 下的重载选择
如果一个 concept 中显式使用了另外一个 concept(可以通过 requires
体中的 requires
语句,也可以通过 concept 定义中的 &&
逻辑连词),则前者比后者严格。如果两个 concept 都能启用模板,则更严格的那个被选择。