【导读】近日,C++20 标准正式公布,这不禁让人想起了 2018 年年底 C++ 标准委员会讨论 C++ 20 新特性的光景。
当时“ C++ 20 还未发布,就已凉凉?”的论调可谓火热,其中 C++ 模块化更是引起了国内外开发者的嫌弃:C++ 开发者怒了:这个无用的模块设计最终会害死 C++!
作者 | 连少华 责编 | 张文
头图 | CSDN 下载自视觉中国
出品 | CSDN(ID:CSDNnews)
近日,ISO C++委员会正式发布了 C++20 标准,命名为 ISO/IEC 14882:2020。
作为程序员,看到新标准发布总想尝鲜,目前 gcc 10.2 可以支持部分 C++ 20 标准,编译的时候需要使用编译选项:-std=c++2a。
1
在类模板和函数模板编程中,主要用于对模板参数的结束和限制,这种约束和限制发生在编译期,编译错误不再那么晦涩难懂了。
在模板编程中,可以限制模板参数的类型或具用某种特性,如:可以限制为整型、数值型、bool 型、或必须支持 hash 特性、或某个类的派生类型等。
在 C++20 中 Concepts 是非常重要的概念,模板编程终于有了质的提升。
Concepts 是 requirements 的具名集合,concepts 需要声明在命名空间中,语法如下:
template < template-parameter-list >concept concept-name = constraint-expression;
如下所示:
templateconcept Hashable = requires(T a) { { std::hash{}(a) } -> std::convertible_to;};//声明了一个名为Hashable的concept
struct meow {};
templatevoid f(T); // 约束这个T必须满足Hashable concept,否则无法编译通过。
int main() { f("abc"s); // OK,string是可hash的 f(meow{}); // Error: meow结构体不是可hash的,当然可以让其支持hash。}//templateconcept C=sizeof(T)>10;
templateclass test{};templatevoid func(T t);
约束是逻辑操作和操作数的序列,它用于指定对模板实参的要求。可在 requires 表达式中出现,也可直接作为 concept 的主体。
有三种类型的约束:
合取(conjunction)
析取(disjunction)
原子约束(atomic constraint)
如下所示:
templatevoid f(T) requires Decrementable;
templatevoid f(T) requires Decrementable; // OK:重声明
requires 用于约束模板参数或具体的参数。
如下所示:
templatevoid f(T&&) requires Eq; // 可作为函数声明符的最末元素出现
template requires Addable // 或在模板形参列表的右边T add(T a, T b) { return a + b; }
关键词 requires 必须后随某个常量表达式(故可以写为 requires true),但其意图是使用某个具名概念(如上例),或具名概念的一条合取/析取,或一个 requires 表达式。
表达式必须具有下列形式之一:
初等表达式,例如 Swappable、std::is_integral::value、(std::is_object_v && …) 或任何带括号表达式
以运算符 && 连接的初等表达式的序列
以运算符 || 连接的前述表达式的序列
语法如下:
requires { requirement-seq } requires ( parameter-list(optional) ) { requirement-seq }
parameter-list - 与函数声明中类似的形参的逗号分隔列表,但不允许默认实参且不能以(并非指定包展开的)省略号结尾。这些形参无存储期、连接或生存期,它们仅用于辅助进行各个要求的制定。这些形参在要求序列的闭 } 前处于作用域中。
requirement-seq - 要求(requirement)的序列,描述于下(每个要求以分号结尾)。
requirement-seq 中的每个要求必须是下面的四项之一:
简单要求(simple requirement)
类型要求(type requirement)
复合要求(compound requirement)
嵌套要求(nested requirement)
如下所示:
templateconcept Addable = requires (T x) { x + x; }; // requires 表达式
template requires Addable // requires 子句,非 requires 表达式T add(T a, T b) { return a + b; }
template requires requires (T x) { x + x; } // 随即的约束,注意关键字被使用两次T add(T a, T b) { return a + b; }
2
Modules (模块)
用于从逻辑上划分代码,能够加快编译速度,并且与导入的顺序无关(还记得以前由于 #include 顺序的不同导致的编译错误吗?)
主要有三个关键字:
module:用于声明一个模块
export:用于导出模块、函数或类
import:用于导入模块
如下所示:
定义了一个 helloworld 模块,导出了 hello 函数
//helloworld.cppexport module helloworld; // module declarationimport ; // import declaration export void hello() { // export declaration std::cout << "Hello world!\n";}
//main.cppimport helloworld;
int main(){ hello();}
3
Coroutines(协程)
协程,就是能够暂停执行然后在接下来的某个时间点恢复执行的函数,C++中的协程是无栈的(stack less)。使用协程可以方便的编写异步代码(和编写同步代码类似)。
主要涉及三个关键字:
co_await
co_await 暂停当前协程的执行,直到等待的操作完成后继续执行。
task<> tcp_echo_server() { char data[1024]; for (;;) { std::size_t n = co_await socket.async_read_some(buffer(data)); #与 Python 中的 await 类似 co_await async_write(socket, buffer(data, n)); }}
上述代码,在 async_read_some() 完成后,继续执行下面的语句,在 async_read_some()执行完成之前,暂停执行并让出控制权。
co_yield
co_yield 暂停执行并返回一个值,与 return 不同的是 co_yield 虽然返回了值 ,但当前函数没有终止。
generator iota(int n = 0) { while(true) co_yield n++; //与 Python 中的 yield 类似}
co_return
co_return 用于结束当前协程的执行并返回一个值
lazy f() { co_return 7;}
当然协程也有一些限制:
不能使用变长实参;
不能使用普通的 return 语句,或占位符返回类型(auto 或 Concept);
constexpr 函数、构造函数、析构函数及 main 函数不能是协程。
4
提供了处理基于范围的元素(可简单理解为容器)的组件及各种适配器,还有一些新的算法。
主要有如下几类:
基于范围的访问器
基于范围的原语
基于范围的 concept
视图
工厂
适配器
详见头文件:
一个简单的例子:
#include #include #include int main(){ std::vector ints{0,1,2,3,4,5}; auto even = [](int i){ return 0 == i % 2; }; auto square = [](int i) { return i * i; }; for (int i : ints | std::views::filter(even) | std::views::transform(square)) { std::cout << i << ' '; }}
5
使用 {} 初始化数组、类、结构体或联合等的成员。
struct A{int a;int b;int c;};A a{.a=10,.b=100,.c=20};
三路比较运算符,形如:
lhs <=> rhs
其行为如下:
(a <=> b) < 0 if lhs < rhs(a <=> b) > 0 if lhs > rhs(a <=> b) == 0 if lhs equal rhs
示例如下:
#include #include int main() { double foo = -0.0; double bar = 0.0; auto res = foo <=> bar; if (res < 0) std::cout << "-0 is less than 0"; else if (res == 0) std::cout << "-0 and 0 are equal"; else if (res > 0) std::cout << "-0 is greater than 0";}
6
[[nodiscard( string-literal )]]:忽略返回值时警告。
[[likely]] 和[[unlikely]]:指示编译器优化更可能出现的情况或分支。是一种对变量值出现可能性的一种预判。
int f(int i){ if (i < 0) [[unlikely]] { return 0; } return 1;}
3. [[no_unique_address]]:用于优化存储空间,当成员为空的时候可以不占用存储空间
7
Others
强制常量初始化,不可以动态初始化
const char * g() { return "dynamic initialization"; }constexpr const char * f(bool p) { return p ? "constant initializer" : g(); }constinit const char * c = f(true); // OKconstinit const char * d = f(false); // error
不再支持以值的形式默认捕获参数;
允许以值的形式显示捕获 this;
支持模板,且支持可变参数;
template void foo(Args... args) { [...xs=args]{ bar(xs...); // xs is an init-capture pack };}
使用 {} 进行格式化字符串,再也不用恶心的 stream 来拼接了,之前使用过boost 的 format,同样好用。
#include #include int main() { std::cout << std::format("Hello {}!\n", "world");}
span 是容器的视图(即不拥有),提供对连续元素组的边界检查访问。因为视图不拥有自己的元素,所以构造和复制的成本很低;
新的线程类,与 std::thread 类似,只是功能更强大,支持停止、自动 join 等
8
更多标准功能详情,大家也可以移步至 C++ 官方发布平台查看。
https://en.cppreference.com/w/cpp/20
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2131r0.html
草案版本:
https://github.com/cplusplus/draft/tree/c++20
作者简介:
连少华,资深架构师。钟爱 C++语言,是 C++语言的资深研究者,对新技术有敏锐洞察和见解,拥有十多年一线软件架构设计和开发经验,先后在中兴通讯、深交所和金证股份任职资深开发和架构师,同时负责软件架构的设计和核心编码。目前在互联网金融企业主导公司交易系统、行情系统和量化系统的设计与开发。先后翻译了《C++代码整洁之道》和《Python 代码整洁之道》
C++代码整洁之道
推荐理由:如果想用C++语言编写出易维护的、扩展性良好的以及生命力强的软件,那么,对于所有的软件开发人员、软件设计人员、对现代C++代码感兴趣或想降低开发成本的项目领导者来说,本书都是必需品。如果你想自学编写整洁的C++代码,那么本书也是你需要的。本书旨在通过一些示例帮助各个技术层次的开发人员编写出易懂的、灵活的、可维护的和高效的C++代码。即使你是一名资深的开发工程师,在本书中也可以找到有价值的知识点。
Python代码整洁之道
推荐理由:本书旨在为不同级别的Python开发人员提供编程技巧方面的指引,以编写出更好的Python软件和程序。所提供的各种技术也不受限于Python的使用领域,同时还展示了如何让代码更符合Python的风格。
RECOMMEND
推荐阅读
更多精彩回顾
书讯 | 1月书讯:Hello 2021! (上)
书讯 | 1月书讯:Hello 2021! (下)
资讯 | TIOBE 1 月编程语言:Python 摘得 2020 年度编程语言!
书单 | 8本书助你了解人民日报“创作大脑”
干货 | 详解华为12种数据采集技术及应用实践
收藏 | 机器学习与人工智能、深度学习有什么关系?终于有人讲明白了
上新 | ECharts开山之作,官方推荐!精心规划适合初学者的ECharts学习路径!
赠书 | 【第37期】Java核心技术经典图书推荐