C++20新特性全解析

C++20新特性全解析

C++20新特性完全指南

#引言

#C++20带来了一系列革命性的改进,从Ranges到Concepts,从协程到模块化系统,这些新特性让C++的开发效率和代码质量都得到了显著提升。

语言特性

#比较运算符<=>

#对于 (a <=> b),如果a > b ,则运算结果>0,如果a < b,则运算结果<0,如果a==b,则运算结果等于0,注意下,运算符的结果类型会根据a和b的类型来决定,所以我们平时使用时候最好直接用auto,方便快捷。

#include

#include

int main() {

double foo = 0.0;

double bar = 1.0;

auto res = foo <=> bar;

if (res < 0)

std::cout << "foo is less than bar";

else if (res == 0)

std::cout << "foo and bar are equal";

else if (res > 0)

std::cout << "foo is greater than bar";}

输出:

foo is less than bar

指定初始化

#struct A { int x; int y; int z; };

A a{.y = 2, .x = 1}; // error; designator order does not match declaration order

A b{.x = 1, .z = 2};

这种乱序初始化方式在C语言中可以,但是在C++中是不可以的,C++里一定要按顺序初始化。

for循环括号里可以初始化

##include

#include

int main() {

std::vector v = {0, 1, 2, 3, 4, 5};

for (const int& i : v) // access by const reference

std::cout << i << ' ';

std::cout << '\n';

for (auto i : v) // access by value, the type of i is int

std::cout << i << ' ';

std::cout << '\n';

for (auto n = v.size(); auto i : v) // the init-statement (C++20)

std::cout << --n + i << ' ';

std::cout << '\n';

}

多了一个char8_t 类型

#和普通的char没区别,就是容易区分出具体大小而已,就像int32_t与int一样。

[[no_unique_address]]

#看着貌似没啥用,没具体关注…

[[likely]]和[[unlikely]]

#在分支预测时,用于告诉编译器哪个分支更容易被执行,哪个不容易执行,方便编译器做优化。

constexpr long long fact(long long n) noexcept {

if (n > 1) [[likely]]

return n * fact(n - 1);

else [[unlikely]]

return 1;}

lambda表达式的捕获

#C++20之前[=]会隐式捕获this,而C++20需要显式捕获,这样[=, this]

struct S2 { void f(int i); };

void S2::f(int i){

[=]{}; // OK: by-copy capture default

[=, &i]{}; // OK: by-copy capture, except i is captured by reference

[=, *this]{}; // until C++17: Error: invalid syntax

// since c++17: OK: captures the enclosing S2 by copy

[=, this] {}; // until C++20: Error: this when = is the default

// since C++20: OK, same as [=]}

lambda表达式可以使用模板

#// generic lambda, operator() is a template with two parameters

auto glambda = [](T a, auto&& b) { return a < b; };

// generic lambda, operator() is a template with one parameter pack

auto f = [](Ts&& ...ts) {

return foo(std::forward(ts)...);

};

consteval

#consteval修饰的函数只会在编译期间执行,如果不能编译期间执行,则编译失败。

consteval int f() { return 42; }

constinit

#断言一个变量有静态初始化,即零初始化和常量初始化,否则程序是有问题的。

const char *g() { return "dynamic initialization"; }

constexpr const char *f(bool p) { return p ? "constant initializer" : g(); }

constinit const char *c = f(true); // OK

// constinit const char *d = f(false); // error

删除了在很多上下文中需要使用typename来消除类型歧义的要求

#template

void workWithIterator(IterT it){

typename std::iterator_traits::value_type tmp(*it); // C++20前

std::iterator_traits::value_type tmp(*it); // C++20

}

结构体直接初始化

#T object {arg1, arg2, ... }; // C++11

T object { .designator = arg1 , .designator { arg2 } ... }; // C++20

协程

#协程具体是什么不做过多介绍,它最大的特点就是可以使用顺序代码的逻辑执行异步的任务,让我们写异步代码非常的方便。

如果一个函数的定义有以下任何一种情况,那么它就是协程:

使用co_await操作符暂停执行,直到恢复task<> tcp_echo_server() {

char data[1024];

for (;;) {

size_t n = co_await socket.async_read_some(buffer(data));

co_await async_write(socket, buffer(data, n));

}

}

使用关键字co_yield暂停执行,返回一个值generator iota(int n = 0) {

while(true)

co_yield n++;

}

使用关键字co_return完成执行,返回一个值lazy f() {

co_return 7;

}

每个协程都必须有一个返回类型来满足以下的许多要求。

示例代码:

#include

#include

#include

#include

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);

}

限制:

协程不能使用可变参数、普通返回语句或占位符返回类型(auto或Concept)。Constexpr函数、构造函数、析构函数和主函数不能是协程。

Modules

#// helloworld.cpp

export module helloworld; // module declaration

import ; // import declaration

export void hello() { // export declaration

std::cout << "Hello world!\n";

}

// main.cpp

import helloworld; // import declaration

int main() {

hello();

}

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

using 可以引用enum

#enum class Animal {

kCat,

kGog

};

int main() {

Animal animal;

using enum Animal;

switch(animal) {

case kCat:

break;

}

}

Constraints and concepts约束和概念

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

这些需求的命名被称为概念。每个概念都是一个谓词,在编译时计算,并成为模板接口的一部分,在那里它被用作约束:

#include

#include

#include

template

concept Hashable = requires(T a) {

{ std::hash{}(a) } -> std::convertible_to;

};

struct meow {};

// Constrained C++20 function template:

template

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

}

缩写函数模板

#void f1(auto); // same as template void f(T)

void f2(C1 auto); // same as template void f2(T), if C1 is a concept

void f3(C2 auto...); // same as template void f3(Ts...), if C2 is a concept

void f4(const C3 auto*, C4 auto&); // same as template void f4(const T*, U&);

template

void g(T x, U y, C auto z); // same as template void g(T x, U y, W z);

新的library特性

#std::format系列

#std::format可用于替代printf,它的目的是补充现有的c++ I/O流库,并重用它的一些基础设施,如用户定义类型的重载插入操作符。

std::string message = std::format("The answer is {}.", 42);

增加日历和时区的支持

#增加了std::atomic:让智能指针线程安全

#source_location:可作为__LINE__ 、__func__这些宏的替代:

##include

#include

#include

void log(const std::string_view message,

const std::source_location& location = std::source_location::current()){

std::cout << "info: "

<< location.file_name() << "("

<< location.line() << ":"

<< location.column() << ") `"

<< location.function_name() << "` "

<< message << '\n';

}

int main(int, char*[]){

log("Hello world!");

}

span:类模板span可以表示一个片段。

##include

#include

#include

#include

template [[nodiscard]]

constexpr auto slide(std::span s, std::size_t offset, std::size_t width) {

return s.subspan(offset, offset + width <= s.size() ? width : 0U);

}

template [[nodiscard]]

constexpr bool starts_with(std::span data, std::span prefix) {

return data.size() >= prefix.size()

&& std::equal(prefix.begin(), prefix.end(), data.begin());

}

template [[nodiscard]]

constexpr bool ends_with(std::span data, std::span suffix) {

return data.size() >= suffix.size()

&& std::equal(data.end() - suffix.size(), data.end(),

suffix.end() - suffix.size());

}

template [[nodiscard]]

constexpr bool contains(std::span span, std::span sub) {

return std::search(span.begin(), span.end(), sub.begin(), sub.end())

!= span.end();

}

void print(const auto& seq) {

for (const auto& elem : seq) std::cout << elem << ' ';

std::cout << '\n';

}

int main(){

constexpr int a[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };

constexpr int b[] { 8, 7, 6 };

for (std::size_t offset{}; ; ++offset) {

constexpr std::size_t width{6};

auto s = slide(std::span{a}, offset, width);

if (s.empty())

break;

print(s);

}

static_assert(starts_with(std::span{a}, std::span{a,4})

&& starts_with(std::span{a+1, 4}, std::span{a+1,3})

&& !starts_with(std::span{a}, std::span{b})

&& !starts_with(std::span{a,8}, std::span{a+1,3})

&& ends_with(std::span{a}, std::span{a+6,3})

&& !ends_with(std::span{a}, std::span{a+6,2})

&& contains(std::span{a}, std::span{a+1,4})

&& !contains(std::span{a,8}, std::span{a,9}));

}

endian:可获取当前平台是大端序还是小端序

##include

#include

int main() {

if constexpr (std::endian::native == std::endian::big) {

std::cout << "big-endian" << '\n';

}

else if constexpr (std::endian::native == std::endian::little) {

std::cout << "little-endian" << '\n';

}

else {

std::cout << "mixed-endian" << '\n';

}

}

make_shared 支持构造数组

#std::remove_cvref看名字就知道,去除CV,去除引用

##include

#include

int main(){

std::cout << std::boolalpha

<< std::is_same_v, int> << '\n'

<< std::is_same_v, int> << '\n'

<< std::is_same_v, int> << '\n'

<< std::is_same_v, int> << '\n'

<< std::is_same_v, int[2]> << '\n'

<< std::is_same_v, int[2]> << '\n'

<< std::is_same_v, int(int)> << '\n';

}

结果全是true

std::to_address:获得由p表示的地址,而不形成对p所指向的对象的引用。

#线程同步:

#barrier:屏障

latch:CountDownLatch

counting_semaphore:信号量

std::jthread:之前的std::thread在析构时如果没有join或者detach会crash,而jthread在析构时会自动join。jthread也可以取消线程:request_stop()。

#C++20也引进了一些中断线程执行的相关类:

stop_token:查询线程是否中断

stop_source:请求线程停止运行

stop_callback:stop_token执行时,可以触发的回调函数

basic_osyncstream:它是对std::basic_syncbuf的再包装,直接使用std::cout多线程下可能出现数据交叉,osyncstream不会发生这种情况。

#{

std::osyncstream synced_out(std::cout); // synchronized wrapper for std::cout

synced_out << "Hello, ";

synced_out << "World!";

synced_out << std::endl; // flush is noted, but not yet performed

synced_out << "and more!\n"; // characters are transferred and std::cout is flushed

}

string的系列操作

#string::starts_with

string::ends_with

string_view::starts_with

string::view::ends_with

#include

#include

#include

template

void test_prefix_print(const std::string& str, PrefixType prefix){

std::cout << '\'' << str << "' starts with '" << prefix << "': " <<

str.starts_with(prefix) << '\n';

}

int main(){

std::boolalpha(std::cout);

auto helloWorld = std::string("hello world");

test_prefix_print(helloWorld, std::string_view("hello"));

test_prefix_print(helloWorld, std::string_view("goodbye"));

test_prefix_print(helloWorld, 'h');

test_prefix_print(helloWorld, 'x');

}

std::assume_aligned

#template< std::size_t N, class T >

[[nodiscard]] constexpr T* assume_aligned(T* ptr);

指定多少字节对齐,来进一步生成有效的代码。

void f(int* p) {

int* p1 = std::assume_aligned<256>(p);

// Use p1, not p, to ensure benefit from the alignment assumption.

// However, the program has undefined behavior if p is not aligned

// regardless of whether p1 is used.

}

bind_front:和使用std::bind绑定第一个参数效果相同

#std::ssize:signed size

##include

#include

#include

int main() {

std::vector v = { 3, 1, 4 };

std::cout << std::size(v) << '\n';

int a[] = { -5, 10, 15 };

std::cout << std::size(a) << '\n';

// since C++20 the signed size (ssize) can avail

auto i = std::ssize(v);

for (--i; i != -1; --i) {

std::cout << v[i] << ' ';

}

std::cout << "\n" "i = " << i << '\n';

}

midpoint 函数计算中位数

#lerp函数计算线性差值:

#constexpr double lerp( double a, double b, double t) noexcept {

return a + t * (b - a);

}

Ranges库:ranges库提供了用于处理元素范围的组件,包括各种视图适配器。

##include

#include

int main(){

auto const ints = {0,1,2,3,4,5};

auto even = [](int i) { return 0 == i % 2; };

auto square = [](int i) { return i * i; };

// "pipe" syntax of composing the views:

for (int i : ints | std::views::filter(even) | std::views::transform(square)) {

std::cout << i << ' ';

}

std::cout << '\n';

// a traditional "functional" composing syntax:

for (int i : std::views::transform(std::views::filter(ints, even), square)) {

std::cout << i << ' ';

}

}

输出:0 4 16

0 4 16

std::is_bounded_array:检查数组是不是有界

#std::is_unbounded_array

##include

#include

class A {};

int main() {

std::cout << std::boolalpha;

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

std::cout << std::is_bounded_array_v << '\n';

}

在numbers头文件中定义了一些数学常量:

#e

log2e

log10e

pi

inv_pi

inv_sqrtpi

ln2

ln10

sqrt2

sqrt3

inv_sqrt3

egamma

phi

贴一张cppreference的截图:

参考资料

https://en.cppreference.com/w/cpp/20

相关推荐

2000w一小时几度电?揭秘功率与电量的换算奥秘!
365bet官网平台

2000w一小时几度电?揭秘功率与电量的换算奥秘!

📅 07-10 👁️ 3684
世界杯巴西队球员分析,桑巴军团阵容深度解析
开彩365下载安装

世界杯巴西队球员分析,桑巴军团阵容深度解析

📅 07-01 👁️ 9724
qq炫舞聚宝盆技巧,qq炫舞聚宝盆和仙露通用的吗【交易】
为何国人这么认可德系车,德系车究竟好在哪?
365体育封号怎么办

为何国人这么认可德系车,德系车究竟好在哪?

📅 07-29 👁️ 8177
电视45英寸长宽是多少厘米(电视45英寸长宽是多少厘米宽)
NECROMANCER-死灵复生漫画免费阅读
365bet官网平台

NECROMANCER-死灵复生漫画免费阅读

📅 09-23 👁️ 9918