输入关键词开始搜索

C++20 Concepts

为什么需要 Concepts

// ❌ C++17 之前:模板错误信息是灾难
template <typename T>
T add(T a, T b) { return a + b; }

vector<int> v;
add(v, v);  // 编译报错 50 行,核心信息是 "vector 不能 +"

// ✅ C++20:编译期检查 + 清晰的报错
template <typename T>
    requires std::is_arithmetic_v<T>
T add(T a, T b) { return a + b; }

add(v, v);  // 报错 1 行:constraint not satisfied: is_arithmetic_v<vector<int>>

定义 Concept

#include <concepts>

// 方式 1: requires 表达式
template <typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;  // a+b 合法且返回值可转为 T
};

// 方式 2: 组合已有 concept
template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

// 方式 3: 类型萃取
template <typename T>
concept Printable = requires(T t, std::ostream &os) {
    { os << t } -> std::same_as<std::ostream &>;
};

// 使用
template <Addable T>
T sum(const std::vector<T> &v) {
    T total{};
    for (const auto &x : v) total += x;
    return total;
}

requires 子句的四种写法

// ① requires 跟在 template 后面(推荐)
template <typename T>
    requires std::integral<T>
T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }

// ② concept 代替 typename
template <std::integral T>
T abs(T x) { return x < 0 ? -x : x; }

// ③ auto 缩写(C++20 简写模板)
auto mul(std::integral auto a, std::integral auto b) { return a * b; }

// ④ 尾置 requires
template <typename T>
T inc(T x) requires std::integral<T> { return x + 1; }

标准库 Concepts

#include <concepts>

// 类型属性
std::integral<T>        // char, int, long ...
std::floating_point<T>  // float, double
std::signed_integral<T>
std::unsigned_integral<T>

// 比较
std::equality_comparable<T>     // 可以 ==
std::totally_ordered<T>         // 可以 < > <= >=

// 可调用
std::invocable<F, Args...>      // F 可以用 Args... 调用
std::predicate<F, Args...>      // F(Args...) 返回 bool

// 复制/移动
std::copyable<T>
std::movable<T>
std::copy_constructible<T>

// 转换
std::convertible_to<From, To>
std::same_as<T, U>              // T 就是 U
std::derived_from<Derived, Base>

实战示例

约束排序函数

template <std::ranges::range R>
    requires std::sortable<std::ranges::iterator_t<R>>
void mySort(R &range) {
    std::ranges::sort(range);
}

vector<int> v{3, 1, 4};
mySort(v);   // ✅
// mySort(cin); // ❌ 漂亮报错

约束容器打印

template <typename T>
concept Container = requires(T c) {
    c.begin();
    c.end();
    typename T::value_type;
};

template <Container C>
    requires Printable<typename C::value_type>
std::ostream &operator<<(std::ostream &os, const C &c) {
    os << "[";
    for (auto it = c.begin(); it != c.end(); ++it) {
        if (it != c.begin()) os << ", ";
        os << *it;
    }
    return os << "]";
}

C++20 其他推荐特性

// Ranges(管道操作)
auto evens = views::iota(1)
           | views::filter([](int n) { return n % 2 == 0; })
           | views::take(5)
           | ranges::to<vector<int>>();
// {2, 4, 6, 8, 10}

// 三路比较 <=>
struct Point {
    int x, y;
    auto operator<=>(const Point &) const = default;
};
Point{1,2} < Point{3,4};  // ✅ 自动生成全部比较运算符

// 字符串 starts_with / ends_with
string s = "hello world";
s.starts_with("he");  // true
s.ends_with("ld");    // true

// contains(容器)
vector<int> v{1,2,3};
v.contains(2);  // true (C++23 才进标准,但很多编译器已支持)