余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

解读C++即将迎来的重大更新(二):C++20的核心语言

xiyangw 2022-11-26 17:03 61 浏览 0 评论

选自modernescpp

作者:JP Tech等

机器之心编译

参与:Panda、杜伟

C++20(C++ 编程语言标准 2020 版)将是 C++ 语言一次非常重大的更新,将为这门语言引入大量新特性。C++ 开发者 Rainer Grimm 通过一系列博客文章介绍 C++20 的新特性。目前这个系列文章已经更新了两篇,本篇是第二篇,主要介绍了 C++20 的核心语言(包括一些新的运算符和指示符)。

C++20 的核心语言 之前的一篇博客概览式地介绍了 C++20 的概念、范围、协程和模块,下面开始介绍它的核心语言。

三路比较运算符 <=> 三路比较运算符 <=> 通常被称为太空船运算符。飞船运算符能确定两个值 A 和 B 谁大谁小或相等。 编译器可以自动生成三路比较运算符。你只需要用 default 礼貌地要求它即可。在这种情况下,你会得到所有六种比较运算符:==、!=、<、 <=、>、>=。

#include?<compare>
struct?MyInt?{
??int?value;
??MyInt(int?value):?value{value}?{?}
??auto?operator<=>(const?MyInt&)?const?=?default;
};


默认的 <=> 执行的是字典顺序比较(lexicographical comparison),使用从基类开始从左到右并以声明顺序(declaration order)使用非静态元素的顺序。
微软的博客上有一些相当复杂精细的示例:https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/

struct?Basics?{
??int?i;
??char?c;
??float?f;
??double?d;
??auto?operator<=>(const?Basics&)?const?=?default;
};
struct?Arrays?{
??int?ai[1];
??char?ac[2];
??float?af[3];
??double?ad[2][2];
??auto?operator<=>(const?Arrays&)?const?=?default;
};
struct?Bases?:?Basics,?Arrays?{
??auto?operator<=>(const?Bases&)?const?=?default;
};
int?main()?{
??constexpr?Bases?a?=?{?{?0,?'c',?1.f,?1.?},
????????????????????????{?{?1?},?{?'a',?'b'?},?{?1.f,?2.f,?3.f?},?{?{?1.,?2.?},?{?3.,?4.?}?}?}?};
??constexpr?Bases?b?=?{?{?0,?'c',?1.f,?1.?},
????????????????????????{?{?1?},?{?'a',?'b'?},?{?1.f,?2.f,?3.f?},?{?{?1.,?2.?},?{?3.,?4.?}?}?}?};
??static_assert(a?==?b);
??static_assert(!(a?!=?b));
??static_assert(!(a?<?b));
??static_assert(a?<=?b);
??static_assert(!(a?>?b));
??static_assert(a?>=?b);
}


我认为,这个代码段中最复杂的部分不是太空船运算符,而是使用聚合初始化(aggregate initialisation)来实现 Base 的初始化。聚合初始化本质上意味着如果所有元素是公开的,那么你可以直接初始化类类型(class、struct 或 union)的元素。在这个案例中,你可以使用示例中那样的 braced-initialisation-list。好吧,这确实经过了简化,详见:https://en.cppreference.com/w/cpp/language/aggregate_initialization 将字符串文字作为模板参数 在 C++20 之前,你不能将字符串用作非类型的模板参数。使用 C++20 时,你则可以这么做。我们可以在标准定义的 basic_fixed_string 中使用它们,其有一个 constexpr 构造函数。这个 constexpr 构造函数能在编译时实例化这个固定字符串。

template<std::basic_fixed_string?T>
class?Foo?{
????static?constexpr?char?const*?Name?=?T;
public:
????void?hello()?const;
};
int?main()?{
????Foo<"Hello!">?foo;
????foo.hello();
}


constexpr 虚拟函数
由于动态类型是未知的,所以无法在常量表达式(constant expression)中调用虚拟函数。这个限制将在 C++20 中被解除。
指定初始化器
我首先谈谈聚合初始化。下面是一个简单示例:

//?aggregateInitialisation.cpp
#include?<iostream>
struct?Point2D{
????int?x;
????int?y;
};
class?Point3D{
public:
????int?x;
????int?y;
????int?z;
};
int?main(){
????std::cout?<<?std::endl;
????Point2D?point2D?{1,?2};
????Point3D?point3D?{1,?2,?3};
????std::cout?<<?"point2D:?"?<<?point2D.x?<<?"?"?<<?point2D.y?<<?std::endl;
????std::cout?<<?"point3D:?"?<<?point3D.x?<<?"?"?<<?point3D.y?<<?"?"?<<?point3D.z?<<?std::endl;
????std::cout?<<?std::endl;
}


我认为无需对这个程序进行解释。看看这个程序的输出:

显式总比隐式好。我们看看这是什么意思。程序 aggregateInitialisation.cpp 中的初始化非常容易出错,因为你可能写反这个构造函数的参数,而且你永远没法察觉。来自 C99 的指定初始化器就能在这里大显身手了。

//?designatedInitializer.cpp
#include?<iostream>
struct?Point2D{
????int?x;
????int?y;
};
class?Point3D{
public:
????int?x;
????int?y;
????int?z;
};
int?main(){
????std::cout?<<?std::endl;
????Point2D?point2D?{.x?=?1,?.y?=?2};
????//?Point2D?point2d?{.y?=?2,?.x?=?1};?????????//?(1)?error
????Point3D?point3D?{.x?=?1,?.y?=?2,?.z?=?2};???
????//?Point3D?point3D?{.x?=?1,?.z?=?2}??????????//?(2)??{1,?0,?2}
????std::cout?<<?"point2D:?"?<<?point2D.x?<<?"?"?<<?point2D.y?<<?std::endl;
????std::cout?<<?"point3D:?"?<<?point3D.x?<<?"?"?<<?point3D.y?<<?"?"?<<?point3D.z?<<?std::endl;
????std::cout?<<?std::endl;
}


实例 Point2d 和 Point3D 的参数从名称就能看出来。这个程序的输出就等同于程序 aggregateInitialisation.cpp 的输出。带注释(1)和(2)的行很有意思。行(1)会报错,因为指定器的顺序与它们的声明顺序不匹配。在(3)行中,y 的指定器缺失了。在这个案例中,y 会被初始化为 0,比如使用 braces-initialisation-list {1, 0, 3}. 对 lambda 的各种改进 C++20 在 lambda 方面的改进也很多。 如果你想要了解改动的细节,请参阅 Bartek 的博客:https://www.bfilipek.com/2019/02/lambdas-story-part1.html,里面介绍了 C++17 和 C++20 中的 lambda 改进。总之,我们会迎来两个有意思的变化。

  • 允许 [=, this] 作为 lambda capture,并通过 [=] 弃用这个的隐式 capture


struct?Lambda?{
????auto?foo()?{
????????return?[=]?{?std::cout?<<?s?<<?std::endl;?};
????}
????std::string?s;
};
struct?LambdaCpp20?{
????auto?foo()?{
????????return?[=,?this]?{?std::cout?<<?s?<<?std::endl;?};
????}
????std::string?s;
};


在 C++20 中,通过在结构体 lambda 中复制而实现隐式 [=] capture 会出现弃用警告。如果你通过复制 [=, this] 来显式地获取它,就不会收到 C++20 的弃用警告。

  • 模板 lambda


你可能和我一样,最先想到的是:我们为什么需要模板 lambda?当你用 C++14 的 [](auto x){ return x; } 写一个通用 lambda 时,编译器会自动使用一个模板化的调用运算符来生成一个类:

template?<typename?T>
T?operator(T?x)?const?{
????return?x;
}


有时候,你想要定义一个只对某个特定类型(如 std::vector)有效的 lambda。现在,模板 lambda 能帮我们做到这一点。你可以不使用类型参数,而是使用概念:

auto?foo?=?[]<typename?T>(std::vector<T>?const&?vec)?{?
????????//?do?vector?specific?stuff
????};


新属性:[[likely]] 和 [[unlikely]] C++20 有 [[likely]] 和 [[unlikely]] 两个新属性。这两个新属性都允许为优化器提供提示:执行的路径是更可能或是更不可能。

for(size_t?i=0;?i?<?v.size();?++i){
??if?(unlikely(v[i]?<?0))?sum?-=?sqrt(-v[i]);
??else?sum?+=?sqrt(v[i]);
}


指示符 consteval 和 constinit 新的指示符 consteval 会创建一个即时函数。对于一个即时函数,每一次函数调用都必然产生一个编译时常量表达式。即时函数是隐式的 constexpr 函数。

consteval?int?sqr(int?n)?{
??return?n*n;
}
constexpr?int?r?=?sqr(100);??//?OK
int?x?=?100;
int?r2?=?sqr(x);?????????????//?Error


因为 x 不是常量表达式,所以最后的赋值会出错。因此,编译时不会执行 sqr(x)。 constinit 会确保有静态存储持续的变量在编译时被初始化。静态存储持续(static storage duration)的意思是对象会在程序开始时分配,在程序结束时又会重新分配。对于命名空间范围内声明的对象(全局对象),声明为 static 或 extern 的对象有静态存储持续。 std::source_location
C++11 有两个宏 __LINE__ 和 __FILE__ 来获取代码行和文件的信息。而在 C++20 中,类 source_location 能提供有关源代码的文件名、行号、列号和函数名信息。下面这个来自 cppreference.com的示例展示了第一种用途:

#include?<iostream>
#include?<string_view>
#include?<source_location>
void?log(std::string_view?message,
?????????const?std::source_location&?location?=?std::source_location::current())
{
????std::cout?<<?"info:"
??????????????<<?location.file_name()?<<?":"
??????????????<<?location.line()?<<?"?"
??????????????<<?message?<<?'\n';
}
int?main()
{
????log("Hello?world!");??//?info:main.cpp:15?Hello?world!
}

相关推荐

“三次握手,四次挥手”你真的懂吗?

记得刚毕业找工作面试的时候,经常会被问到:你知道“3次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就没有下文了,面试官貌似也没有深入下去的意...

面试官问:三次握手与四次挥手是怎么完成的?

作者|饶全成来源|码农桃花源记得刚毕业找工作面试的时候,经常会被问到:你知道“3次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就没有...

三次握手和四次挥手的高阶面试题,建议收藏

昨天村长的讲解,真是一语点醒,这样的解释胜过死记硬背。但对于学习者,如果不能有直观感受,可能还是觉得不接地气,今天介绍两个工具,一个是网络抓包工具Wireshark,一个是linux命令tcpdum...

三次握手和四次挥手到底是个什么鬼东西

之前总有是有面试官喜欢问,你知道什么是三次握手么?什么是四次挥手么?为什么握手需要三次,挥手需要四次呢?今天我们就来详细的聊一下这个。1.什么是TCPTCP协议,简单称呼一下的话,那就是传输控制协议,...

加深理解TCP的三次握手与四次挥手

在了解三次握手和四次挥手之前,先要知道TCP报文内部包含了那些东西。熟悉了解TCP报文对日后学习网络和排除方面有很大的帮助,所以,今天为了加深对三次握手的理解,从新去认识TCP报文格式。TCP报文格式...

三次握手 与 四次挥手_三次握手四次挥手大白话

三次握手:①首先Client端发送连接请求报文②Server段接受连接后回复ACK报文,并为这次连接分配资源。③Client端接收到ACK报文后也向Server段发生ACK报文...

动画讲解TCP的3次握手,4次挥手,让你一次看明白

专注于Java领域优质技术,欢迎关注作者:老钱占小狼博客TCP三次握手和四次挥手的问题在面试中是最为常见的考点之一。很多读者都知道三次和四次,但是如果问深入一点,他们往往都无法作出准确回答。本篇尝试...

linux下实现免密传输文件或登录到其他服务器

使用scp传输文件到其他服务器的时候,提示需要输密码,如下:[root@18csetup]#scpLINUX.X64_180000_db_home.zip192.168.133.120:/u0...

Linux如何通过salt免密SCP传输上百台机的脚本?看chatGPT的回答

如何通过salt免密SCP传输上百台机的shell脚本”,下面是chatGPT给出的结果。scp批量免密脚本给出的详细shell脚本如下:#!/bin/bash#源文件路径和目标路径SRC_...

Linux/Mac scp命令上传文件_将hdfs上的文件下载到本地的命令是

语法scp[可选参数]file_sourcefile_target参数说明:-1:强制scp命令使用协议ssh1-2:强制scp命令使用协议ssh2-4:强制scp命令只使用IPv4寻...

Linux常用功能——文件远程传输_linux 远程传输文件

scp是securecopy的简写,是linux系统下基于ssh登陆进行安全的远程文件拷贝命令,用于在Linux下进行远程拷贝文件的命令。和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器...

使用 scp 命令定时拉取服务器备份文件

我们的服务器,每周五必须要做下备份,但总是忘记执行备份这件事情,或者是服务器备份做了,但没有做异地备份。所以通过定时任务自动备份,备份成功之后,在其它服务器上面通过定时任务scp命令自动拉取备份文...

windows下最轻便的FTP/SCP文件管理器

这次推荐的工具叫做winscp,这个工具如果是IT从业人员,又是做服务端相关工作的话,可能无人不知,如果是刚入门,推荐立马上手试试。如果看了觉得有用,欢迎收藏、点赞、关注。官方网站:https://w...

我不是网管 - Linux中使用SCP命令安全复制文件

SCP是linux发行版中的命令行工具,用于通过网络安全地跨系统复制文件和目录。SCP代表安全复制,因为它使用ssh协议复制文件。拷贝时,scp命令建立ssh连接到远程系统。换句话说...

WinSCP软件双系统(Win-Linux)文件传输教程

WinSCP软件是windows下的一款使用ssh协议的开源图形化SFTP客户端,也就是一个文件传输的软件,它有什么优点吗,咱们嵌入式开发中经常会将windows中的文件复制到linux系统当中,比较...

取消回复欢迎 发表评论: