C++20 重量级的特性

文摘   2024-12-06 12:47   新加坡  

点击上方蓝字 江湖评谈设为关注/星标




前言

鉴于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 <concepts>#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 <iostream>#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 <iostream>#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 <iostream>#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 <ranges>#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 <iostream>#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用处何在?

关注公众号↑↑↑:江湖评谈

江湖评谈
记录,分享,自由。
 最新文章