C++20最重要的三个新特性

496次阅读  |  发布于2年以前

C++之父都说过,C++20是C++语言的一次重大变革,引入了大量的新特性。

这其中个人认为最重要新特性是这三个:

Modules

直接看代码:


// helloworld.cpp
export module helloworld;  // module declaration
import <iostream>;         // import declaration
export void hello() {      // export declaration
    std::cout << "Hello world!\n";
}

// main.cppimport helloworld;  // import declaration
int main() {
    hello();
}

每个C++开发者应该都知道include方式是将header中的代码拷贝一份到源文件中,在大的工程项目中还有很多冗余的include,种种原因,导致编译速度相当的慢,而modules却大大改善了这种问题。

modules使用方式和include差不多,但modules使用比include头文件速度更快,C++全球开发者大会中,C++之父贴出来过测试数据,modules效率比include高了25倍。

以后modules肯定会是主流使用方式。

Coroutines

协程是一种比线程和进程更高效的多任务处理模型。

在C++20中,终于把协程引了进来,协程具体还分为有栈协程和无栈协程,两者对比,无栈协程是更高效的协程。

而C++20支持的就是无栈协程,为此提供了三个关键字:

这是一段cppreference上,协程相关的示例代码:


#include <coroutine>#include <iostream>#include <stdexcept>#include <thread>
auto switch_to_new_thread(std::jthread& out) {
  struct awaitable {
    std::jthread* p_out;
    bool await_ready() { return false; }
    void await_suspend(std::coroutine_handle<> h) {
      std::jthread& out = *p_out;
      if (out.joinable())
        throw std::runtime_error("Output jthread parameter not empty");
      out = std::jthread([h] { h.resume(); });
      // Potential undefined behavior: accessing potentially destroyed *this
      // std::cout << "New thread ID: " << p_out->get_id() << '\n';
      std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK
    }
    void await_resume() {}
  };
  return awaitable{&out};
}

struct task{
  struct promise_type {
    task get_return_object() { return {}; }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };
};

task resuming_on_new_thread(std::jthread& out) {
  std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
  co_await switch_to_new_thread(out);
  // awaiter destroyed here
  std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
}

int main() {
  std::jthread out;
  resuming_on_new_thread(out);
}

目前C++20只是从语法层面对协程做了支持,还没有相关的协程库,要想使用协程,还需要引入协程库,但不可否认,C++20已经支持了协程,以后在这方面肯定会越来越完善。

前一段时间参加的C++全球开发者大会,C++之父也说过会全力在C++23标准中引入对协程库的支持。

Concepts

Concepts在模板编程中起到重大的作用, 类模板、函数模板和非模板函数(通常是类模板的成员)可以与一个约束相关联,这个约束指定了对模板实参的要求,这些实参可用于选择最合适的函数重载和模板特化。

很多人应该都知道SFINAE,在C++20前多数都在使用std::enable_if,这相当的麻烦,代码可读性也不高,编译器报错信息也不是很友好,而有了Concepts就方便的多, 每个Concepts都是一个谓词,在编译时计算,并成为模板接口的一部分,在那里它被用作约束:


#include <string>#include <cstddef>#include <concepts>
template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}

int main() {
  using std::operator""s;
  f("abc"s); // OK, std::string satisfies Hashable
  //f(meow{}); // Error: meow does not satisfy Hashable
}

其实C++20还有一些特性我比较喜欢,比如std::jthread,真真切切的解决了std::thread存在的旧有问题。

还有fmt,因为cout方式输出复杂的格式很麻烦:比如我想输出a + b = c:

std::cout << a << " + " << b << " = " << c << std::endl;

是不是很麻烦,那可以使用printf?

printf("%d + %d = %d \n", a, b, c);

但printf需要开发者填入变量格式,如果填写有误,有可能打印错误,也有可能crash

所以使用printf会给开发者带来负担,一定要在打印前确认变量的类型,并且一定要保证配置的输出格式正确,我想大多数人可能都会在print某个变量前搜索过打印某个类型需要使用哪种格式吧

那用啥呢?cout配合format

std::cout << std::format("{} + {} = {} \n", a, b, c);

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8