一、The Big Four
1.1 Concepts
C++中模板编程极大地简化了代码的编写,但同时也会带来一些使用上的疑惑,例如一下代码片段,判断两个数是否相等,只要是重载了==
运算符,自定义类型也可以使用该模板实例化对应的比较函数:
/**
* @file comcept.cpp
* @brief
* @author YongDu
* @date 2021-07-20
*/
template <typename T>
auto isEqual(T left, T right) {
return left == right;
}
int main() {
cout << std::boolalpha;
double a = 2.334;
double b = 2.335;
cout << isEqual(2, 2) << endl; // true
cout << isEqual(b - a, 0.001) << endl; // false
return 0;
}
我们知道,浮点数因为在存储中涉及精度问题,所以不能直接去判断两个浮点数是否想定,需要自定义精度去判断。模板函数依然可以正确执行,但结果却不是我们想要的,因此我们需要使用该模板的类型做一些限制。假如说我们限定次函数的类型为整型,当然,这只是举个例子,如果单纯地限定为整型,也就没有使用模板的必要。我们可以采用C++11中的静态断言static_asser
,该断言会在编译期执行检测,判断类型是否为整型,如下:
template <typename T>
auto isEqual(T left, T right) {
static_assert(std::is_integral<T>::value);
return left == right;
}
此时使用浮点数进行比较,便会触发断言,编译失败,如下图所示。还有个问题就是此时断言在函数内部,也就是说必须通过模板实例化对应函数,然后执行该函数时才能触发断言,某些情况下很可能编译成功,在运行期才会发生错误。
C++17提供了concept机制,用来检查模板参数类型,代码如下:
template <typename T>
concept Integeral = std::is_integral<T>::value;
template <Integeral T>
auto isEqual(T left, T right) {
return left == right;
}
int main() {
cout << std::boolalpha;
double a = 2.334;
double b = 2.335;
cout << isEqual(2, 2) << endl; // true
cout << isEqual(b - a, 0.001) << endl; // false
return 0;
}
此时的报错信息更清晰,方便了我们去排查错误。如上的concept使用方法还有以下3种写法,效果是一样的,有人推崇第四种写法,省略了typename关键字,但是我觉得第一种写法更清晰:
// #2
template <typename T>
requires Integeral<T>
auto isEqual(T left, T right) {
return left == right; }
// #3
template <typename T>
auto isEqual(T left, T right) requires Integeral<T> {
return left == right;
}
// #4
Integeral auto isEqual(Integeral auto left, Integeral auto right) {
return left == right; }
1.2 Range library
ranges
:代表一段元素,之前版本使用begin
和end
标识一段元素,那么用ranges有什么好处呢?
- 简化语法和操作;
- 防止begin,end迭代器的不配对使用;
- 使得类似管道
|
的串行操作成为可能。
相关概念:
- View:延迟计算,只有读权限
- Actions:即时处理,读或写
- Algorithms:操作range
- Views和Actions的串联操作
具体看代码(需要包含<ranges>
头文件):
简化操作:
std::sort
和std::ranges::sort
比较
std::vector<int> vec{
3, 1, 2, 5, 6, 4};
// std::sort()
std::sort(vec.begin(), vec.end());
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>{
cout, " "}); // 1 2 3 4 5 6
// ranges::sort()
std::ranges::sort(vec);
std::ranges::copy(vec, std::ostream_iterator<int>{
cout, " "}); // 1 2 3 4 5 6
串联视图:
定义了一个vector,even用来筛选偶数,square返回参数的平方
std::vector<int> vec{
1, 2, 3, 4, 5, 6};
auto even = [](int i) {
return i % 2 == 0; };
auto square = [](int i) {
return i * i; };
auto result = vec | std::views::filter(even) | std::views::transform(square);
/* 此时 result 并没有被计算,只是定义了一系列视图,只有到下面的遍历操作时,result 触发,即所谓延迟计算 */
for (auto i : result) {
cout << i << " "; // 4 16 36
}
过滤和变换:
std