c++ constraints与concepts使用笔记

ops/2025/3/12 8:46:37/

1. 模板参数缺乏约束的问题

问题分析

  • 传统模板参数没有语法层面的约束,需要程序员自行通过代码逻辑理解参数要求
  • 编译器错误信息不友好,尤其在传递非法参数时(如 vector<int&>
  • 类型检查发生在模板实例化时,而非声明时

示例

template<typename T>
class Container {T data[10];
public:void copy_from(const Container& other) {std::copy(std::begin(other.data), std::end(other.data), data);}
};struct NonCopyable {NonCopyable(const NonCopyable&) = delete;
};Container<NonCopyable> c;  // 编译错误出现在实例化时的copy操作,而非类定义处

2. Concepts 基本概念

核心特性

  • C++20 引入的编译期谓词机制
  • 通过 requires 子句显式约束模板参数
  • 提升代码可读性和编译器错误信息质量

示例

template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;template<Arithmetic T>  // 约束T必须是算术类型
T add(T a, T b) { return a + b; }add(3, 5);      // OK
add("a", "b");  // 明确的编译错误:不满足Arithmetic约束

3. Concept 的定义与使用

(1) 单参数 Concept

template<typename T>
concept HasSize = requires(T v) {{ v.size() } -> std::convertible_to<size_t>;
};template<HasSize T>
void print_size(T obj) {std::cout << obj.size() << "\n";
}std::vector v{1,2,3};
print_size(v);  // OK
print_size(42); // 错误:int没有size()方法

(2) 多参数 Concept

template<typename T, typename U>
concept SameAs = std::is_same_v<T, U>;template<typename T>
concept AddableToInt = requires(T a) {{ a + 1 } -> SameAs<int>;  // 使用两参数Concept
};AddableToInt auto result = 'A' + 1;  // OK,char + int 返回int

4. requires 表达式详解

(1) 简单表达式

template<typename T>
concept Incrementable = requires(T v) {++v;        // 检查前置++v++;        // 检查后置++
};static_assert(Incrementable<int>);     // 通过
static_assert(Incrementable<std::string>); // 失败

(2) 类型表达式

template<typename T>
concept HasValueType = requires {typename T::value_type;  // 检查嵌套类型是否存在
};static_assert(HasValueType<std::vector<int>>);  // 通过
static_assert(HasValueType<int>);               // 失败

(3) 复合表达式

template<typename T>
concept StringConvertible = requires(T obj) {{ std::to_string(obj) } -> std::same_as<std::string>;
};static_assert(StringConvertible<int>);    // 通过
static_assert(StringConvertible<void*>);  // 失败

(4) 嵌套表达式

template<typename T>
concept CompleteType = requires {sizeof(T);        // 检查类型完整性requires !std::is_void_v<T>;  // 组合多个条件
};static_assert(CompleteType<int>);      // 通过
static_assert(CompleteType<void>);    // 失败

5. requires 从句 vs requires 表达式

关键区别

// requires 从句(用于模板参数约束)
template<typename T>
requires std::is_integral_v<T>  // ← 这是requires从句
void process(T value) { /*...*/ }// requires 表达式(用于定义Concept的约束条件)
template<typename T>
concept Streamable = requires(T v, std::ostream& os) {{ os << v } -> std::same_as<std::ostream&>;
};

完整示例:约束矩阵运算

template<typename T>
concept Numeric = std::is_arithmetic_v<T> && !std::is_same_v<T, bool>;template<typename M>
concept Matrix = requires(const M& mat, size_t i, size_t j) {{ mat.rows() } -> std::convertible_to<size_t>;{ mat.cols() } -> std::convertible_to<size_t>;{ mat(i,j) } -> Numeric;typename M::value_type;requires Numeric<typename M::value_type>;
};template<Matrix A, Matrix B>
auto multiply(const A& a, const B& b) {using T = std::common_type_t<typename A::value_type, typename B::value_type>;std::vector<std::vector<T>> result(a.rows(), std::vector<T>(b.cols()));// ... 矩阵乘法实现return result;
}

优势

  1. 显式约束矩阵类型必须具有 rows(), cols() 方法
  2. 元素访问操作 operator() 必须返回数值类型
  3. 矩阵元素类型必须满足 Numeric 约束
  4. 编译错误会明确指出具体违反的约束条件

通过合理使用 Concepts 和 requires 表达式,可以显著提升模板代码的可维护性和错误信息的可读性,同时增强接口的自我描述能力。


http://www.ppmy.cn/ops/165159.html

相关文章

C++【类和对象】(超详细!!!)

C【类和对象】 1.运算符重载2.赋值运算符重载3.日期类的实现 1.运算符重载 (1).C规定类类型运算符使用时&#xff0c;必须转换成调用运算符重载。 (2).运算符重载是具有特殊名字的函数&#xff0c;名字等于operator加需要使用的运算符&#xff0c;具有返回类型和参数列表及函数…

Redis常用数据结构及命令详解:从基础到进阶

Redis作为一款高性能的键值存储系统&#xff0c;凭借其丰富的数据结构和灵活的用法&#xff0c;被广泛应用于缓存、队列、计数器等场景。Redis提供了多种数据结构&#xff0c;每种结构都有其独特的特性和适用场景。本文将详细介绍Redis的几种常用数据结构&#xff0c;并指出使用…

基于USB Key的Web系统双因素认证解决方案:构建安全与便捷的登录体系

摘要 在网络安全威胁日益严峻的背景下&#xff0c;传统的“用户名密码”认证方式已难以应对钓鱼攻击、密码窃取等风险。上海安当基于USB Key技术&#xff0c;推出了一套面向Web系统的双因素认证解决方案&#xff0c;通过硬件与密码学的深度融合&#xff0c;实现用户身份的高强度…

【每日八股】计算机网络篇(四):HTTP

目录 HTTP 与 HTTPS 的区别&#xff1f;HTTPS 加密与认证的过程&#xff1f;ClientHelloServerHello客户端回应服务端回应 HTTPS 一定安全可靠吗&#xff1f;HTTPS 状态码的含义&#xff1f;HTTP 缓存有哪些实现方式&#xff1f;HTTP 1.0、HTTP 1.1、HTTP 2.0 和 HTTP 3.0 的区…

MyBatis的级联查询(一对一、一对多、多对多)

MyBatis的级联查询 级联的优点是获取关联数据十分便捷。但是级联过多会增加系统的复杂度&#xff0c;同时降低系统的性能&#xff0c;此增彼减。所以记录超过 3 层时&#xff0c;就不要考虑使用级联了&#xff0c;因为这样会造成多个对象的关联&#xff0c;导致系统的耦合、负…

numpy常用函数详解

在深度神经网络代码中经常用到numpy库的一些函数&#xff0c;很多看过之后很容易忘记&#xff0c;本文对经常使用的函数进行归纳总结。 np.arange arange是numpy一个常用的函数&#xff0c;该函数主要用于创建等差数列。它的使用方法如下所示&#xff1a; numpy.arange([star…

llama.cpp编译

llam.cpp编译 1. 下载&编译 git clone https://github.com/ggml-org/llama.cpp cmake -S . -B build2. 下载模型验证 # 下载地址 https://huggingface.co/filipealmeida/open-llama-7b-v2-open-instruct-GGUF/blob/main/ggml-model-Q4_0.gguf# 验证 ./llama-cli.exe -m …

Python第十六课:深度学习入门 | 神经网络解密

🎯 本节目标 理解生物神经元与人工神经网络的映射关系掌握激活函数与损失函数的核心作用使用Keras构建手写数字识别模型可视化神经网络的训练过程掌握防止过拟合的基础策略一、神经网络基础(大脑的数字化仿生) 1. 神经元对比 生物神经元人工神经元树突接收信号输入层接收特…