点击上方蓝字 江湖评谈设为关注/星标
前言
鉴于Rust发布了1.83.0,本来想玩玩C++29和C++32的新特性,虽有部分提案释出来,也可以玩。但C++29在2029年发布,C++32大约在2032年发布。然后再看看目前主流编译器VS2022 C++默认的版本是C++14,当然附带支持了C++17,C++20以及C++23的一个预览版。再来看看GCC13.2.0也仅支持了部分C++23的特性,其余在逐步增加当中。综合估量了下,目前最稳定,支持最全面,最新的C++版本也就是C++20了。所以本篇看下个人认为的C++20一些重量级的特性。
C++20特性
1.引入了模版约束(Concepts)
一:Concepts
Concepts这个特性的引入,可以使我们更细致化的操作传给模板的类型,并且可以让模板类型满足特定的条件。比如下面我们让模板类型支持int约束操作,浮点型操作则编译报错。
#include <iostream>
//定义一个 Concept,要求 T 是一个整数类型
template<typename T>
concept Integral = std::integral<T>;
//使用 Concept 约束模板参数
void print_double(Integral auto x) {
std::cout << "Double: " << x * 2 << '\n';
}
int main() {
print_double(42); // 正确:42是int类型
//print_double(3.14); // 编译器提示错误:没有与参数匹配的模板列表,3.14 不是整数类型
return 0;
}
二.传统的C++模板
Concepts相较于传统的C++模板代码,我们看下传统的C++模板:
#include <concepts>
template <typename T>
T print_double(T a) {
std::cout << "Double: " << a * 2 << '\n';
return a;
}
int main() {
//无论是int还是浮点型都可以运行
print_double(5);
print_double(3.14);
return 0;
}
传统的C++模板代码更直观,我们调用了整形和浮点类型,都计算出了结果。跟Concepts的约束截然不同。
那么这就是问题所在,某些时候,某些场景或者某些项目中。我们的模板不需要计算浮点类型的结果,或者说我们的模板不让它支持浮点类型的计算。如果以传统模板的方式,要么增加代码,要么添加逻辑。而Concepts约束模板类型此时就显得非常有用了,它完全满足某些特殊的场景,更加精细化的控制模板。这也是它的价值所在。同时也使得代码更加安全、可读且易于维护。
2.类型别名模板(Template Type Aliases)
顾名思义:可以为类型模板定义一个简洁的名称。C++11已经引入了类型别名模板,C++20继续增强此类功能。以下例子:
#include <map>
template <typename K, typename V>
using MyMap = std::map<K, V>;
int main() {
MyMap<int, std::string> mapExample;
mapExample[1] = "One";
mapExample[2] = "Two";
for (const auto& pair : mapExample) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
在此示例中,我们使用了 MyMap<int, std::string>
代替了 std::map<int, std::string>
。这个类型别名模板使得代码更加简洁和可读。
3.三向比较运算符(Spaceship Operator)
三向比较运算符会自动生成 <, <=, >, >=, == 和 != 操作符,如以下例子,比较结构体Point是否相等,可以通过operator重载三向比较运算符来计算。例子:
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 自动生成所有比较操作符
};
int main() {
Point p1{ 1, 2 }, p2{ 2, 3 };
if (p1 <=> p2 == 0) {
std::cout << "Equal\n";
}
else {
std::cout << "Not Equal\n";
}
getchar();
}
如下代码:
auto operator<=>(const Point&) const = default;
自动生成了所有的比较运算符,你只要对Point类型进行比较,它都会得出正确的结果。
4.C++20 提供了新的 ranges
库
通过ranges库,可以对数据进行筛选,过滤,映射,链式操作,懒加载等。如下筛选出了vector<int>数组里面%2等于0的数字结果乘以2,例子:
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
auto even_nums = nums | std::ranges::views::filter([](int n){ return n % 2 == 0; })
| std::ranges::views::transform([](int n){ return n * 2; });
for (int num : even_nums) {
std::cout << num << " ";
}
}
题外话
在测试C++20的Concepts特性的时候,遇到一个比较隐秘的问题。先来看下代码:
#include <concepts>
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() {
std::cout << "int add: " << add(5, 10) << std::endl; // 输出:15
// 下面这行代码会编译错误,因为 std::string 不符合 Addable 概念
// std::string s1 = "Hello", s2 = "World";
// std::cout << add(s1, s2) << std::endl; // 编译错误
return 0;
}
我们看下注释的这部分,如下:
// 下面这行代码会编译错误,因为 std::string 不符合 Addable 概念
// std::string s1 = "Hello", s2 = "World";
// std::cout << add(s1, s2) << std::endl; // 编译错误
C++20测试当中,它不在Concepts特性的约束下。实际上add(s1, s2)这段代码不仅编译不会错误,而且还会正常运行。那么问题在哪儿呢?在于std::string类型的字符串也是可以做加法运算的。
如果以这个例子说明Concepts约束,无疑是失败的,因为它并没有被约束到。但是我们把这段代码改成如下:
std::cout << add("Hello", "World") << std::endl; // 编译错误
这个时候,它就受到了Concepts特性的约束了,编译器会提示错误指针不能相加。因为此时add函数的参数变成了const char*类型。指针类型是不可以做加法运算的。
但const char*就能作为Concepts特性的例子吗?依然不能,下面我们继续看为什么会这样?
相较于传统的C++模板,Concepts最大的好处就是约束模板的参数,进行定制化的操作。std::string类型不会受到Concepts加法运算的约束,const char*则会受到Concepts加法运算的约束,但同时也会受到传统模板加法的约束,如果把const char*以参数形式传给传统模板进行加法操作,则编译器提示:“+”: 不能添加两个指针,例子如下:
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
cout << add(3, 4) << endl;
cout << add("Hello", "World") << endl; // 编译器提示不能相加两个指针
return 0;
}
这是一个常理,指针是不能够相加的。const char*不仅受到Concepts特性的约束,也受到传统模板的约束。
所以说如果以两个字符串相加举例子说明Concepts特性约束是失败的。这是一个小细节,比较重要,在此记录。
结尾
本篇记录了C++20引入新的,比较重要的四大特性。分别是模板约束(Concepts),类型别名模板,三向比较运算符,新的 ranges
库。C++20引入新特性较多,这里只是部分展示个人认为较为重量级的。作为基础设施的C++,重要性还是有的。
意犹未尽?关注我,带你玩遍各大热门编程语言。
往期精彩回顾
Rust会取代C++吗?
C++安全指针,Rust用处何在?
关注公众号↑↑↑:江湖评谈