网站首页 > 博客文章 正文
模板非常适合编写与不同类型一起工作的代码。例如,这个函数可以与任何数值类型一起工作:
template <typename T>
T arg42(const T & arg) {
return arg + 42;
}
但是,当你尝试用非数值类型调用它时会发生什么呢?
const char * n = "7";
cout << "result is " << arg42(n) << "\n";
输出:
Result is ion
这可以编译并运行,但结果是不可预测的。实际上,这个调用是危险的,它很容易崩溃或变成一个漏洞。我更希望编译器生成一个错误信息,这样我可以修复代码。
现在,有了概念,我可以这样写:
template <typename T>
requires Numeric<T>
T arg42(const T & arg) {
return arg + 42;
}
`requires` 关键字是 C++20 的新特性。它对模板应用约束。Numeric 是一个概念,它只接受整数和浮点类型。现在,当我用非数值参数编译这段代码时,我会得到一个合理的编译器错误:
error: 'arg42': no matching overloaded function found
error: 'arg42': the associated constraints are not satisfied
这样的错误信息比大多数编译器错误更有用。
让我们更仔细地看看如何在代码中使用概念和约束。
如何做到这一点…
概念其实就是一个命名的约束。上面的 Numeric 概念看起来像这样:
#include <concepts>
template <typename T>
concept Numeric = integral<T> || floating_point<T>;
这个概念要求类型 T 满足 std::integral 或 std::floating_point 预定义概念中的任何一个。这些概念包含在 <concepts> 头文件中。
概念和约束可以用在类模板、函数模板或变量模板中。我们已经看到了一个受约束的函数模板,现在这里有一个简单的受约束的类模板示例:
template<typename T>
requires Numeric<T>
struct Num {
T n;
Num(T n) : n{n} {}
};
这里有一个简单的变量模板示例:
template<typename T>
requires floating_point<T>
T pi{3.1415926535897932385L};
你可以在任何模板上使用概念和约束。让我们考虑一些更进一步的例子。为了简单起见,我们将在这些例子中使用函数模板。
约束可以使用概念或类型特征来评估一个类型的特性。你可以使用 <type_traits> 头文件中的任何类型特征,只要它返回一个 bool。
例如:
template<typename T>
requires is_integral<T>::value // value 是 bool
constexpr double avg(vector<T> const& vec) {
double sum{ accumulate(vec.begin(), vec.end(), 0.0) };
return sum / vec.size();
}
`requires` 关键字是 C++20 的新特性。它为模板参数引入了一个约束。在这个例子中,约束表达式测试模板参数是否符合类型特征 is_integral。
你可以使用 <type_traits> 头文件中的预定义特性,或者你可以像定义模板变量一样定义你自己的,只要它返回 constexpr bool。例如:
template<typename T>
constexpr bool is_gt_byte{ sizeof(T) > 1 };
这定义了一个类型特征,称为 is_gt_byte。这个特性使用 sizeof 运算符来测试类型 T 是否大于 1 字节。
概念其实就是一组命名的约束。例如:
template<typename T>
concept Numeric = is_gt_byte<T> &&
(integral<T> || floating_point<T>);
这定义了一个名为 Numeric 的概念。它使用我们的 is_gt_byte 约束,以及来自 <concepts> 头文件的 floating_point 和 integral 概念。我们可以使用它来限制模板只接受大小大于 1 字节的数值类型。
template<Numeric T>
T arg42(const T & arg) {
return arg + 42;
}
你会注意到,我在模板声明中应用了约束,而不是在 requires 表达式中的单独一行。有几种方法可以应用概念。让我们看看这是如何工作的。
它是如何工作的…
有几种不同的方法可以应用概念或约束:
你可以使用 requires 关键字应用概念或约束:
template<typename T>
requires Numeric<T>
T arg42(const T & arg) {
return arg + 42;
}
你可以在模板声明中应用概念:
template<Numeric T>
T arg42(const T & arg) {
return arg + 42;
}
你可以在函数签名中使用 requires 关键字:
template<typename T>
T arg42(const T & arg) requires Numeric<T> {
return arg + 42;
}
或者你可以在简写函数模板的参数列表中使用概念:
auto arg42(Numeric auto & arg) {
return arg + 42;
}
对于许多目的来说,选择这些策略中的一种可能是风格问题。在某些情况下,一个可能比另一个更好。
还有更多…
标准使用术语 conjunction(合取)、disjunction(析取)和 atomic(原子)来描述可以用来构建约束的表达式类型。让我们定义这些术语。
你可以使用 && 和 || 运算符组合概念和约束。这些组合分别称为合取和析取。你可以将它们视为逻辑 AND 和 OR。
通过使用 && 运算符与两个约束结合,形成约束合取:
Template <typename T>
concept Integral_s = Integral<T> && is_signed<T>::value;
合取只有在 && 运算符的两边都满足时才满足。它从左到右进行评估。合取的操作数是短路的,也就是说,如果左边的约束不满足,右边的将不会被评估。
通过使用 || 运算符与两个约束结合,形成约束析取:
Template <typename T>
concept Numeric = integral<T> || floating_point<T>;
析取如果 || 运算符的任一边满足就满足。它从左到右进行评估。合取的操作数是短路的,也就是说,如果左边的约束满足,右边的将不会被评估。
原子约束是一个返回 bool 类型的表达式,不能再进一步分解。换句话说,它不是合取也不是析取。
template<typename T>
concept is_gt_byte = sizeof(T) > 1;
你也可以在原子约束中使用逻辑 !(NOT)运算符:
template<typename T>
concept is_byte = !is_gt_byte<T>;
正如预期的那样,! 运算符将 ! 右边的 bool 表达式的值取反。
当然,我们可以将所有这些表达式类型组合成一个更大的表达式。我们在以下示例中看到了这些约束表达式的例子:
template<typename T>
concept Numeric = is_gt_byte<T> &&
(integral<T> || floating_point<T>);
让我们分解一下。子表达式 (integral<T> || floating_point<T>) 是一个析取。子表达式 is_gt_byte<T> && (…) 是一个合取。而每个子表达式 integral<T>、floating_point<T> 和 is_gt_byte<T> 都是原子的。
这些区分主要是为了描述目的。虽然理解细节是好的,但在你编写代码时,将它们视为简单的逻辑 ||、&& 和 ! 运算符是安全的。
概念和约束是 C++ 标准中受欢迎的补充,我期待着在未来的项目中使用它们。
猜你喜欢
- 2024-10-12 C++核心准则T.24:用标签类或特征区分只有语义不同的概念
- 2024-10-12 用苹果发布会方式打开C++20(苹果在哪开发布会)
- 2024-10-12 C++核心准则T.25:避免互补性约束(规矩是一种约束,一种准则)
- 2024-10-12 C++核心准则T.21:为概念定义一套完整的操作
- 2024-10-12 C++核心准则T.5:结合使用泛型和面向对象技术应该增强效果
- 2024-10-12 C++经典书籍(c++相关书籍)
- 2024-10-12 C++一行代码实现任意系统函数Hook
- 2024-10-12 C++核心准则T.11:只要可能就使用标准概念
- 2024-10-12 C++核心准则T.48:如果不能用概念,用enable_if
- 2024-10-12 C++核心准则T.13:简单、单类型参数概念使用缩略记法更好
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)