• 售前

  • 售后

热门帖子
入门百科

C++ 20 标准都发布了哪些重要特性?

[复制链接]
顺势而为47 显示全部楼层 发表于 2021-7-24 16:11:16 |阅读模式 打印 上一主题 下一主题
近日,ISO C++ 委员会正式发布了 C++20 标准,命名为 ISO/IEC 14882:2020。

作为程序员,看到新标准发布总想实战一番,目前 gcc 10.2 可以支持部分 C++ 20 标准,编译的时候需要使用编译选项:-std=c++2a。

大家可以升级gcc 10.2 以支持c++20的主要特性:

点此下载gcc10.2第一部分
点此下载gcc10.2第二部分
点此下载gcc10.2第三部分

Constraints and concepts (约束和概念)

在类模板和函数模板编程中,主要用于对模板参数的结束和限制,这种约束和限制发生在编译期,编译错误不在那么晦涩难懂了。

在模板编程中,可以限制模板参数的类型或具用某种特性,如:可以限制为整型、数值型、bool型、或必须支持hash特性、或某个类的派生类型等。


在C++20中Concepts是非常重要的概念,模板编程终于有了质的提升。


Concepts

Concepts是requirements的具名集合,concepts需要声明在命名空间中,语法如下:

  1. template < template-parameter-list >
  2. concept concept-name = constraint-expression;
复制代码

如下所示:

  1. template<typename T>
  2. concept Hashable = requires(T a) {
  3.     { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
  4. };//声明了一个名为Hashable的concept
  5. struct meow {};
  6. template<Hashable T>
  7. void f(T); // 约束这个T必须满足Hashable concept,否则无法编译通过。
  8. int main() {
  9.   f("abc"s); // OK,string是可hash的
  10.   f(meow{}); // Error: meow结构体不是可hash的,当然可以让其支持hash。
  11. }
  12. //
  13. template<typename T>
  14. concept C=sizeof(T)>10;
  15. template<C T>
  16. class test{};
  17. template<C T>
  18. void func(T t);
复制代码
Constraints

约束是逻辑操作和操作数的序列,它用于指定对模板实参的要求。可在 requires 表达式中出现,也可直接作为concept的主体。

有三种类型的约束:

  • 合取(conjunction)
  • 析取(disjunction)
  • 原子约束(atomic constraint)
  • 如下所示:
    1. template<Incrementable T>
    2. void f(T) requires Decrementable<T>;
    3. template<Incrementable T>
    4. void f(T) requires Decrementable<T>; // OK:重声明
    复制代码


Requires

requires 用于约束模板参数或具体的参数。

requires 子句

如下所示:

  1. template<typename T>
  2. void f(T&&) requires Eq<T>; // 可作为函数声明符的最末元素出现
  3. template<typename T> requires Addable<T> // 或在模板形参列表的右边
  4. T add(T a, T b) { return a + b; }
复制代码

关键词 requires 必须后随某个常量表达式(故可以写为 requires true),但其意图是使用某个具名概念(如上例),或具名概念的一条合取/析取,或一个 requires 表达式。表达式必须具有下列形式之一:
  • 初等表达式,例如 Swappable、std::is_integral::value、(std::is_object_v && …) 或任何带括号表达式
  • 以运算符 && 连接的初等表达式的序列
  • 以运算符 || 连接的前述表达式的序列
  • requires 表达式
  • 语法如下:
    1. requires { requirement-seq }               
    2. requires ( parameter-list(optional) ) { requirement-seq }               
    复制代码
    parameter-list - 与函数声明中类似的形参的逗号分隔列表,但不允许默认实参且不能以(并非指定包展开的)省略号结尾。这些形参无存储期、连接或生存期,它们仅用于辅助进行各个要求的制定。这些形参在 要求序列 的闭 } 前处于作用域中。
  • requirement-seq - 要求(requirement)的序列,描述于下(每个要求以分号结尾)。
  • requirement-seq中的每个要求必须是下面的四项之一:
  • 简单要求(simple requirement)
  • 类型要求(type requirement)
  • 复合要求(compound requirement)
  • 嵌套要求(nested requirement)

    如下所示:

    1. template<typename T>
    2. concept Addable = requires (T x) { x + x; }; // requires 表达式
    3. template<typename T> requires Addable<T> // requires 子句,非 requires 表达式
    4. T add(T a, T b) { return a + b; }
    5. template<typename T>
    6.     requires requires (T x) { x + x; } // 随即的约束,注意关键字被使用两次
    7. T add(T a, T b) { return a + b; }
    复制代码
    Modules (模块)

    用于从逻辑上划分代码,能够加快编译速度,并且与导入的顺序无关(还记得以前由于#include顺序的不同导致的编译错误吗?)
    主要有三个关键字:


    • module:用于声明一个模块
    • export:用于导出模块、函数或类
    • import:用于导入模块

    如下所示:


    定义了一个helloworld模块,导出了hello函数


    1. //helloworld.cpp
    2. export module helloworld;  // module declaration
    3. import <iostream>;         // import declaration
    4. export void hello() {      // export declaration
    5.     std::cout << "Hello world!\n";
    6. }
    复制代码
    1. //main.cpp
    2. import helloworld;
    3. int main()
    4. {
    5.     hello();
    6. }
    复制代码
    Coroutines(协程)

    协程,就是能够暂停执行然后在接下来的某个时间点恢复执行的函数,C++中的协程是无栈的(stack less)。使用协程可以方便的编写异步代码(和编写同步代码类似)。


    主要涉及三个关键字:

    • co_await
      co_await暂停当前协程的执行,直到等待的操作完成后继续执行。
      1. task<> tcp_echo_server() {
      2.   char data[1024];
      3.   for (;;) {
      4.     std::size_t n = co_await socket.async_read_some(buffer(data)); #与 Python 中的 await 类似
      5.     co_await async_write(socket, buffer(data, n));
      6.   }
      7. }
      复制代码

      上述代码,在async_read_some()完成后,继续执行下面的语句,在 async_read_some()执行完成之前,暂停执行并让出控制权。


      • co_yield
        co_yield暂停执行并返回一个值,与return不同的是co_yield虽然返回了值 ,但当前函数没有终止。
        1. generator<int> iota(int n = 0) {
        2.   while(true)
        3.     co_yield n++;  //与 Python 中的 yield 类似
        4. }
        复制代码

      • co_return
        co_return 用于结束当前协程的执行并返回一个值
        1. lazy<int> f() {
        2.   co_return 7;
        3. }
        复制代码
        当然协程也有一些限制:
      • 不能使用变长实参
      • 不能使用普通的 return 语句,或占位符返回类型(auto 或 Concept)
      • constexpr 函数、构造函数、析构函数及 main 函数 不能是协程
      • Ranges(范围)
      • 提供了处理基于范围的元素(可简单理解为容器)的组件及各种适配器,还有一些新的算法。
        主要有如下几类:


        • 基于范围的访问器
        • 基于范围的原语
        • 基于范围的concept
        • 视图
        • 工厂
        • 适配器

        详见头文件:

        一个简单的例子:

        1. #include <vector>
        2. #include <ranges>
        3. #include <iostream>
        4. int main()
        5. {
        6.     std::vector<int> ints{0,1,2,3,4,5};
        7.     auto even = [](int i){ return 0 == i % 2; };
        8.     auto square = [](int i) { return i * i; };
        9.     for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
        10.         std::cout << i << ' ';
        11.     }
        12. }
        复制代码


      • Designated Initializers(指定初始化)

        使用{}初始化数组、类、结构体或联合等的成员。

        1. struct A{int a;int b;int c;};
        2. A a{.a=10,.b=100,.c=20};
        复制代码
        operator<=>

        三路比较运算符,形如:

        1. lhs <=> rhs
        复制代码

        其行为如下:

        1. (a <=> b) < 0 if lhs < rhs
        2. (a <=> b) > 0 if lhs > rhs
        3. (a <=> b) == 0 if lhs equal rhs
        复制代码

        示例如下:

        1. #include <compare>
        2. #include <iostream>
        3. int main() {
        4.     double foo = -0.0;
        5.     double bar = 0.0;
        6.     auto res = foo <=> bar;
        7.     if (res < 0)
        8.         std::cout << "-0 is less than 0";
        9.     else if (res == 0)
        10.         std::cout << "-0 and 0 are equal";
        11.     else if (res > 0)
        12.         std::cout << "-0 is greater than 0";
        13. }
        复制代码
        Attributes(特性)
        • [[nodiscard( string-literal )]]
          忽略返回值时警告。
        • [[likely]] 和[[unlikely]]
          指示编译器优化更可能出现的情况或分支。是一种对变量值出现可能性的一种预判。
          1. int f(int i)
          2. {
          3.     if (i < 0) [[unlikely]] {
          4.         return 0;
          5.     }
          6.     return 1;
          7. }
          复制代码

  • [[no_unique_address]]
  • 用于优化存储空间,当成员为空的时候可以不占用存储空间
  • Others
  • constexpr 新增对虚函数的支持。
  • char8_t 用于存储utf-8的字符串。
  • constinit
  • 强制常量初始化,不可以动态初始化
    1. const char * g() { return "dynamic initialization"; }
    2. constexpr const char * f(bool p) { return p ? "constant initializer" : g(); }
    3. constinit const char * c = f(true); // OK
    4. constinit const char * d = f(false); // error
    复制代码

  • labmda
  • 不再支持以值的形式默认捕获参数;
    允许以值的形式显示捕获this;
    支持模板,且支持可变参数;
    1. template <typename... Args>
    2. void foo(Args... args) {
    3.         [...xs=args]{
    4.                 bar(xs...); // xs is an init-capture pack
    5.         };
    6. }
    复制代码
    std::format
  • 使用{}进行格式化字符串,再也不用恶心的stream来拼接了,之前使用过boost的format,同样好用。
    1. #include <iostream>
    2. #include <format>
    3. int main() {
    4.     std::cout << std::format("Hello {}!\n", "world");
    5. }
    复制代码
    std::span
  • span是容器的视图(即不拥有),提供对连续元素组的边界检查访问。因为视图不拥有自己的元素,所以构造和复制的成本很低;
  • std::jthread
  • 新的线程类,与std::thread类似,只是功能更强大,支持停止、自动join等
  • Calendar 和 time zoneendian 用于判断大小端的枚举std::make_shared 支持数组
  • atomic支持浮点数和smart ptrstd::basic_syncbuf 和std::basic_osyncstreamstring 增加starts_with和end_with函数
  • std::atomic_ref 原子引用std::to_array 将xxx转换为std::arrayinline namespace
  • 特性概览
  • core language features
  • library features
  • 更多标准功能详情,大家也可以移步至C++ 官方发布平台:


    草案版本:



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作