当前位置: 首页 > news >正文

Concepts (C++20)


C++20 Concepts

Concepts 是 C++20 引入的核心特性,用于显式约束模板参数,提升代码可读性和错误提示。以下通过代码示例和原理分步骤解析其用法。


1. 基本概念
  • 目标:显式声明模板参数必须满足的条件。
  • 优势:替代复杂的 SFINAE 和 enable_if,直接约束类型。

2. 定义 Concept

使用 concept 关键字定义约束条件。

示例 1:检查类型是否可打印
#include <iostream>
#include <concepts>template <typename T>
concept Printable = requires(std::ostream& os, T val) {{ os << val } -> std::same_as<std::ostream&>;
};
  • 说明:要求类型 T 必须支持 operator<< 输出到流。

3. 应用 Concept 到函数模板
示例 2:约束函数参数可打印
void print(Printable auto val) {std::cout << val << std::endl;
}int main() {print(42);       // 合法:int 可打印print("Hello");  // 合法:const char* 可打印// print(std::vector<int>{}); // 错误:vector 不满足 Printable
}

4. 标准库预定义 Concepts

使用 <concepts><ranges> 中的标准 Concepts。

示例 3:约束算术类型
template <typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;Arithmetic auto square(Arithmetic auto val) {return val * val;
}int main() {square(5);      // 合法:int 是整数类型square(3.14);   // 合法:double 是浮点类型// square("5"); // 错误:const char* 不满足 Arithmetic
}

5. 组合 Concepts

使用逻辑运算符组合多个 Concepts。

示例 4:约束类型可加且结果匹配
template <typename T>
concept Addable = requires(T a, T b) {{ a + b } -> std::same_as<T>;
};template <Addable T>
T add(T a, T b) {return a + b;
}int main() {add(3, 4);      // 合法:int 满足 Addable// add("a", "b"); // 错误:const char* 不满足 Addable
}

6. 在类模板中使用 Concepts
示例 5:约束容器元素可默认构造
#include <concepts>template <std::default_initializable T>
class SafeContainer {T data;
public:SafeContainer() = default;
};int main() {SafeContainer<int> c1;      // 合法:int 可默认构造// SafeContainer<std::unique_ptr<int>> c2; // 错误:unique_ptr 不可默认构造
}

7. 复杂约束与嵌套要求
示例 6:约束类型有 size() 方法且返回整型
template <typename T>
concept HasSize = requires(T t) {{ t.size() } -> std::integral;
};void printSize(HasSize auto obj) {std::cout << obj.size() << std::endl;
}struct Vec {size_t size() const { return 10; }
};int main() {Vec v;printSize(v);           // 合法:Vec 有 size() 返回 size_t// printSize(42);       // 错误:int 没有 size()
}

8. 标准库 Concepts 示例

直接使用标准库中的 Concepts。

示例 7:使用 std::sort 约束可排序范围
#include <vector>
#include <algorithm>
#include <ranges>void sortAndPrint(std::ranges::sortable auto& range) {std::sort(range.begin(), range.end());
}int main() {std::vector<int> vec{5, 3, 1, 4, 2};sortAndPrint(vec);      // 合法:vector 支持随机访问迭代器// std::list<int> lst{5, 3}; // sortAndPrint(lst);  // 错误:list 迭代器不支持随机访问
}

9. 原理与编译器行为
  • 替换检查:编译器在模板实例化时验证 Concept 约束。
  • 错误提示:直接指出违反的约束条件,而非模板实例化失败。
  • 零开销:所有检查在编译期完成,无运行时负担。

总结

操作语法示例用途
定义 Concepttemplate<typename T> concept C = ...;声明模板参数的显式约束
函数模板约束void func(C auto param)限制参数类型满足 Concept
组合 ConceptsC1 && C2 或 `C1
类模板约束template<C T> class X {};约束类模板参数
标准库 Conceptsstd::integral<T>, std::sortable快速实现通用约束

Concepts 显著提升了模板代码的可读性和健壮性,结合现代 C++ 特性(如简写模板),使得泛型编程更加直观高效。


多选题


题目 1:Concepts 的基本约束与函数模板重载

以下代码的输出是什么?

#include <iostream>
#include <concepts>template <typename T>
concept HasValue = requires(T t) { t.value; };struct A { int value; };
struct B {};void process(HasValue auto obj) { std::cout << "HasValue" << std::endl; }
void process(auto obj) { std::cout << "Generic" << std::endl; }int main() {A a;B b;process(a); // 调用哪个版本?process(b); // 调用哪个版本?return 0;
}

A. HasValueGeneric
B. GenericGeneric
C. 编译失败,重载冲突
D. 运行时错误


题目 2:组合 Concepts 的逻辑

以下代码是否能编译通过?

#include <concepts>template <typename T>
concept Integral = std::integral<T>::value;template <typename T>
concept Floating = std::floating_point<T>::value;template <typename T>
concept Number = Integral<T> || Floating<T>;template <Number T>
T add(T a, T b) { return a + b; }int main() {auto x = add(3, 4);       // intauto y = add(3.5, 4.5);   // doubleauto z = add("a", "b");   // const char*return 0;
}

A. 编译成功
B. 编译失败,add("a", "b") 不满足 Number
C. 编译失败,Number 的约束定义错误
D. 运行时错误


题目 3:标准库 Concepts 与迭代器约束

以下代码的输出是什么?

#include <vector>
#include <list>
#include <ranges>template <typename T>
void checkRange(T&& range) {if constexpr (std::ranges::random_access_range<T>) {std::cout << "Random Access";} else if constexpr (std::ranges::bidirectional_range<T>) {std::cout << "Bidirectional";} else {std::cout << "Generic";}
}int main() {std::vector<int> vec;std::list<int> lst;checkRange(vec);  // 输出什么?checkRange(lst);  // 输出什么?return 0;
}

A. Random AccessBidirectional
B. Random AccessGeneric
C. BidirectionalBidirectional
D. 编译失败,约束不合法


题目 4:嵌套要求与复杂约束

以下代码是否能编译通过?

#include <concepts>template <typename T>
concept ComplexCheck = requires(T t) {requires std::integral<decltype(t.value)>;{ t.print() } -> std::same_as<void>;
};struct Valid { int value; void print() {} };
struct Invalid1 { double value; void print() {} };
struct Invalid2 { int value; };ComplexCheck auto process(auto obj) { obj.print(); }int main() {process(Valid{});     // 合法?process(Invalid1{});  // 合法?process(Invalid2{});  // 合法?return 0;
}

A. 仅 Valid 合法
B. ValidInvalid1 合法
C. 所有调用均合法
D. 编译失败,ComplexCheck 定义错误


题目 5:Concepts 与模板特化的优先级

以下代码的输出是什么?

#include <iostream>
#include <concepts>template <typename T>
concept Large = sizeof(T) > 4;void process(Large auto t) { std::cout << "Large"; }
void process(auto t) { std::cout << "Generic"; }int main() {process(10);      // int(sizeof(int) = 4)process(10L);     // long(sizeof(long) = 8)return 0;
}

A. GenericLarge
B. LargeLarge
C. GenericGeneric
D. 编译失败,重载冲突


答案与解析


题目 1:Concepts 的基本约束与函数模板重载

答案:A
解析

  • process(HasValue auto) 的约束更严格,优先于无约束的 process(auto)
  • A 满足 HasValue(有 value 成员),调用第一个版本;B 不满足,调用第二个版本。
  • 选项 C 错误:Concepts 约束的函数和非约束函数可以合法重载,优先选择更特化的版本。

题目 2:组合 Concepts 的逻辑

答案:B
解析

  • Number 约束 T 必须是整数或浮点类型。
  • add("a", "b") 中的 const char* 不满足 Number,导致编译失败。
  • 选项 C 错误Number 定义正确,Integral<T> || Floating<T> 语法合法。

题目 3:标准库 Concepts 与迭代器约束

答案:A
解析

  • std::vector 的迭代器是随机访问迭代器,输出 Random Access
  • std::list 的迭代器是双向迭代器,输出 Bidirectional
  • 选项 D 错误:标准库 Concepts 的定义合法。

题目 4:嵌套要求与复杂约束

答案:A
解析

  • ComplexCheck 要求:
    1. t.value 的类型必须满足 std::integral
    2. t.print() 必须存在且返回 void
  • 只有 Valid 满足所有条件:
    • Invalid1value 类型是 double,不满足 std::integral
    • Invalid2 没有 print() 方法。

题目 5:Concepts 与模板特化的优先级

答案:A
解析

  • process(10)intsizeof 为 4,不满足 Large,调用 Generic
  • process(10L)longsizeof 为 8,满足 Large,调用 Large
  • 选项 D 错误:Concepts 约束的函数和非约束函数可以合法重载,无冲突。

总结

通过这组题目,可以深入理解 Concepts 的以下核心机制:

  1. 重载优先级:约束更严格的函数优先匹配。
  2. 组合逻辑:通过 &&|| 组合多个 Concepts。
  3. 标准库集成:如 std::ranges::random_access_range
  4. 嵌套要求:使用 requires 子句细化约束条件。
  5. 编译时决策:基于 sizeof 等编译期属性选择实现。

相关文章:

  • 如何在 Postman 中,自动获取 Token 并将其赋值到环境变量
  • 每日c/c++题 备战蓝桥杯 ([洛谷 P1226] 快速幂求模题解)
  • Java 富文本转word
  • java方法引用
  • static成员
  • jQuery的removeClass(),一次删除多个class
  • 4.2 Prompt工程与任务建模:高效提示词设计与任务拆解方法
  • 【学习笔记】文件包含漏洞--相关习题
  • 全面解析 UGC 平台物品冷启动策略
  • 【Linux内核】内核中的中断管理
  • Activepieces - 开源自动化工具
  • 【动手学大模型开发】什么是大语言模型
  • 【阿里云大模型高级工程师ACP习题集】2.4 自动化评测答疑机器人的表现(⭐️⭐️⭐️ 重点章节!!!)
  • Java Collections工具类指南
  • 计算机组成与体系结构:直接内存映射(Direct Memory Mapping)
  • Spring Boot YML配置值“011“在代码中变为9的问题解析
  • leetcode 2799. 统计完全子数组的数目 中等
  • 玩转Docker | Docker部署LMS轻量级音乐工具
  • 加深对vector理解OJ题
  • MQTT 之 EMQX
  • 成都一季度GDP为5930.3亿元,同比增长6%
  • “70后”女博士张姿卸任国家国防科技工业局副局长
  • 上海咖啡消费有多“嗲”?咖啡馆已逾9000家,咖啡节主市集持续4天
  • 西安雁塔区委书记王征拟任市领导班子副职,曾从浙江跨省调任陕西
  • 庆祝中国印尼建交75周年招待会暨万隆会议70周年纪念活动在京举行
  • 沙龙 | 新书分享:中国电商崛起的制度密码