介绍为保持数十年的兼容性而设计的关键当代 C++ 机制。
搜索 博客@CACM 软件工程和编程语言 21 世纪 C++ 介绍为保持数十年的兼容性而设计的关键当代 C++ 机制。
作者:Bjarne Stroustrup 发布2025 年 2 月 4 日 深色背景中的 C++ 代码和单词 C++ 从 C++ 诞生至今,已有 45 多年。正如计划的那样,C++ 不断发展以应对挑战,但许多开发人员使用 C++ 的方式却好像它仍停留在上个千年。从表达想法的简易性、性能、可靠性和可维护性的角度来看,这并不是最理想的。在这里,我介绍了可以构建高性能、类型安全和灵活的 C++ 软件的关键概念:资源管理、生命周期管理、错误处理、模块化和泛型编程。最后,我介绍了确保代码是现代的,而不是依赖过时、不安全且难以维护的技术的方法:指南和配置文件。
介绍 C++ 理念 资源管理 模块化 通用编程 指导方针和执行 未来 概括 参考 1. 简介 C++ 是一种历史悠久的语言。这导致许多开发人员、教师和学者忽视了几十年来的进步,并把 C++ 描述成今天仍然是第二个千年,那时的电话必须插在墙上,而且大多数代码都很短、很低级、很慢。
如果您的操作系统几十年来一直保持兼容性,那么您可以在现代计算机上运行 1985 年编写的 C++ 程序。稳定性(与早期版本的 C++ 兼容)非常重要,尤其是对于维护软件系统数十年的组织而言。然而,在几乎所有情况下,当代 C++ 30 都可以更简单地表达这种旧式代码中所体现的思想,并提供更好的类型安全保证,并且使用更少的内存让它们运行得更快。
这篇文章介绍了为实现这一目标而设计的关键当代 C++ 机制。在最后(§6),它描述了强制使用这种现代 C++ 的技术。
考虑一个简单的程序,它将输入中的每一行唯一地写入输出:
import std; // make all of the standard library available using namespace std;
int main() // print unique lines from input
{ unordered_map<string,int> m; // hash table for (string line; getline (cin,line); ) if (m[line]++ == 0) cout<<line<<'\n'; } 行家会认出这是 AWK 程序(!a[$0]++)。 它使用unordered_map(哈希表的 C++ 标准库版本)来保存唯一行,并且仅在第一次看到某行时才输出。
for语句用于将循环变量(行)的范围限制在循环内。
与旧的 C++ 风格相比,值得注意的是缺少显式的:
分配/取消分配 尺寸 错误处理 类型转换(强制类型转换) 指针 不安全的下标 预处理器的使用(特别是没有#include)。 尽管如此,与旧式程序相比,该程序效率相当高,比大多数程序员在合理的时间内编写的程序效率更高。如果需要更高的性能,可以对其进行调整。C++ 的一个重要方面是,具有合理接口的代码可以根据特定需求进行调整,甚至可以使用专用硬件。这可以在不干扰其他代码的情况下完成,并且通常无需修改编译器。
考虑该程序的一个变体,它收集唯一的行以供以后使用:
import std;
using namespace std; // make all of the standard library available
vector
unordered_set s; // hash table
for (string line; getline(is,line); )
s.insert(line);
return vector{from_range, s}; // copy set elements into a vector
} auto lines = collect_lines(cin); 由于不需要计数,因此我使用了集合而不是映射。我返回的是向量而不是集合,因为向量是最广泛使用的容器。我不需要指定向量的元素类型,因为编译器会根据集合的元素类型推断出它。
我使用from_range参数来告诉编译器和人类读者使用了一个范围,而不是其他可能的初始化向量的方式。我本来希望使用逻辑上最小的向量{m},但标准委员会决定要求from_range对许多人来说会有所帮助。
经验丰富的程序员会注意到这个collect_lines()会复制读取的字符。这可能是一个性能问题,因此在 §3.2 中,我将展示如何调整collect_lines()以避免这种情况。
这些小例子有什么意义呢?在深入技术细节之前,先展示一些当代的 C++,希望能让人们摆脱几十年来自满的错误理解。 返回顶部
- C++ 理念 我对 C++ 的目标可以概括为
直接表达想法 静态类型安全 资源安全(又称“无泄漏”) 直接访问硬件 性能(又称效率) 可承受的可扩展性(又称零开销抽象) 可维护性(又称可理解的代码) 平台独立性(又称可移植性) 稳定性(又称兼容性) 这一点自 C++ 诞生之初就没有改变过24,但 C++ 是需要不断发展的,而且当代 C++ 可以在代码中比早期版本的 C++ 更好地提供这些属性。
体现这些理念的 C++ 代码并非仅仅通过应用所有最新功能就能实现。一些关键功能和技术已经过时
具有构造函数和析构函数的类 例外 模板 std::vector … 其他主要功能较新
模块(§4) 概念(用于指定通用接口;§5.1) Lambda 表达式(用于生成函数对象;§5.1) 范围(§5.1) Constexpr 和 consteval (用于编译时计算;§5.2) 并发支持和并行算法 协程(几十年来一直被忽视,人们认为它们是早期 C++ 的重要组成部分) std::shared_ptr … 重要的是以适合要解决的问题的方式将语言和库功能作为一个连贯的整体来使用。
编程语言的价值在于其应用范围和质量。对于 C++ 来说,证据就是其惊人的应用范围:基础软件、图形、科学应用、电影、游戏、汽车、语言实现(而不仅仅是 C++ 实现)、飞行控制、搜索引擎、浏览器、半导体设计和制造、太空探测器、金融、人工智能等等。鉴于 C++ 有数十亿行代码,我们无法对 C++ 语言进行不兼容的更改。但是,我们可以改变 C++ 的使用方式。
在这篇文章的其余部分,我将重点关注
资源管理(包括生命周期控制和错误处理) 模块(包括消除预处理器) 通用编程(包括概念) 指导方针和执行(我们如何保证我们所写的确实是“21 世纪 C++”?) 当然,这并不是 C++ 提供的全部功能,许多优秀代码都是以这里未列出的方式编写的。例如,我没有提及面向对象编程,因为许多开发人员知道如何在 C++ 中很好地实现这一点。此外,高性能代码和直接操作硬件的代码需要特别的关注和技术。广泛的 C++ 并发支持值得至少单独写一篇论文。然而,大多数优秀软件的关键是类型安全的接口,它包含足够的信息以允许优化和运行时检查编译时无法保证的属性。
返回顶部
3.资源管理 资源是我们必须获取并随后显式或隐式释放(归还)的任何东西。例如,内存、锁、文件句柄、套接字、线程句柄、事务和着色器。为了避免资源泄漏,我们必须避免手动/显式释放。众所周知,人们(甚至是程序员)都不擅长记住归还他们借来的东西。
管理资源的基本 C++ 技术是将其根植于句柄中,该句柄保证在句柄的作用域离开时释放资源。为了可靠性,我们不能依赖应用程序代码中的显式delete、free()、unlock()等。此类操作属于资源句柄。请考虑:
template
void fct()
{
Vector
这种构造函数-析构函数对的使用(通常称为 RAII - “资源获取即初始化”)不仅可以保证释放资源,还可以最大限度地减少资源保留,因此与许多其他技术(例如依赖垃圾收集器的内存管理)相比,具有显着的性能优势。
3.1. 寿命控制 控制表示资源的对象的生命周期对于简单高效的资源管理是必要的。C++ 提供了 4 个控制点,定义为对类(此处称为X)的操作:
构造:首次使用前调用:建立类不变量(如果有)。名称:构造函数,X(可选参数) 析构:最后一次使用后调用:释放所有资源(如果有)。名称:析构函数,~X() 复制:创建一个与另一个对象具有相同值的新对象;a=b意味着a==b(对于常规类型)。名称:复制构造函数、X(const X&)和复制赋值、X::operator=(const X&) 移动:将资源从一个对象移动到另一个对象,通常在作用域之间移动。名称:移动构造函数、X(X&&)和移动赋值、X::operator=(X&&) 例如,我们可以像这样阐述我们的Vector :
template
3.2. 消除冗余副本 有了这个框架,我们再看一下§1中的collect_lines示例。首先,我们可以稍微简化一下:
vector
这里,vector被移出collect_lines()而不是被复制。vector 的移动构造函数的最坏成本是 6 个字复制,其中三个用于复制表示,三个用于将原始表示清零。如果 vector 有 100 万个元素,情况仍然如此。
在许多情况下,即使是这么小的成本也会被消除。自 1983 年左右以来,编译器已经知道在目标(此处为lines )中构造返回值(此处为vector{from_range,s}; ) 。这被称为“复制省略”。
但是,字符串仍然会从集合复制到向量中。这可能会很昂贵。原则上,编译器可以推断出我们在创建向量后不再使用s,而只是移动字符串元素,但如今的编译器并不那么聪明,所以我们必须明确要求移动:
vector
3.3. 资源和错误 C++ 的主要目标之一是资源安全:不泄漏任何资源。这意味着我们必须在错误情况下防止资源泄漏。基本规则是:
不要泄露资源 不要让资源处于无效状态 因此,当检测到无法在本地处理的错误时,在退出函数之前我们必须:
使每个访问的对象处于有效状态 释放该函数负责的每个对象 将其留给调用链上的某些函数来处理与资源相关的问题 这意味着“原始指针”不能可靠地用作资源句柄。考虑一个可能保存内存、锁和文件句柄等资源的Gadget类型:
void f(int n, int x) { Gadget g {n}; Gadget* pg = new Gadget{n}; // explicit new: don’t! // … if (x<100) throw std::runtime_error{"Weird!"}; // leaks *pg; but not g if (x<200) return; // leaks *pg; but not g // … } 明确使用new将Gadget放置在堆上是一个问题,因为它的结果存储在“原始指针”中,而不是具有合适析构函数的资源句柄中。本地对象比使用显式new更简单,通常更快。
对于可靠的系统,我们需要一个明确的错误处理策略。通常,最好的方法是区分可由直接调用者本地处理的错误和只能在调用链上游处理的错误。
使用错误代码和测试来处理常见且可以在本地处理的故障
对罕见(“异常”)且无法在本地处理的故障使用异常
另一种选择是昂贵的“错误代码地狱”,其中每个调用者在调用堆栈上都必须记住测试 未能检查异常会导致终止,而不是错误的结果 在一些重要的应用程序中,无条件立即终止不是一种选择。那么,我们必须记住测试每个错误返回代码并在某处捕获每个异常(例如,在main()中)并执行所需的任何适当响应。
令很多人惊讶的是,即使对于小型系统14,5,异常也比一致使用错误代码更便宜、更快捷。
基于异常的错误处理不适用于用作资源句柄的指针。为了获得简单、可靠且可维护的错误处理,我们必须依赖异常和 RAII 以及错误代码来处理应该在本地处理的错误。请考虑:
void fct(jthread& prod, jthread& cons, string name)
{
ifstream in { name };
if (!in) { /* … */ } // possible failure expected
// …
vector
不幸的是,异常并没有得到普遍的重视,也没有在适当的地方得到使用。除了过度使用“裸”指针之外,许多开发人员坚持使用单一技术来报告所有错误也是一个问题。也就是说,所有错误都通过抛出或返回错误代码来报告。这不符合实际代码的需求。
返回顶部
4.模块化 C++ 从 C 继承而来的预处理器基本上被广泛使用,但它是工具开发和编译器性能的主要障碍。在当代 C++ 中,用于表达常量、函数和类型的宏已被正确类型和范围的常量、编译时求值的函数和模板30 所取代。然而,预处理器对于表达弱形式的模块化至关重要。库和其他单独编译的代码的接口表示为包含 C++ 源文本和#include 的文件。
4.1. 头文件
include指令将源文本从此类“头文件”复制到当前翻译单元。不幸的是,这意味着
include "a.h"
include "b.h"
可能与
include "b.h"
include "a.h"
这就是细微错误的根源。
include是可传递的。也就是说,如果ah包含#include “ch”,则 ch的文本也会成为使用#include “ah”的每个源文件的一部分。这是造成隐患的原因。由于头文件通常在数十或数百个源文件中被#include,这也意味着需要进行多次重复编译。
4.2. 模块 使用头文件伪造模块化的问题早在 C++ 诞生之前就已为人所知,但定义替代方案并将其引入数十亿行代码并非易事。不过,C++ 现在提供了可提供适当模块化的模块。导入模块与顺序无关,因此
import a; import b; 意思是相同的
import b; import a; 模块之间的相互独立意味着代码卫生的改善。它使得细微的依赖性错误不可能发生。
这是一个非常简单的模块定义示例:
export module map_printer; // we are defining a module
import iostream; // we import modules needed for the implementation
import containers;
using namespace std;
export // this template is the only entity exported
template
模块只需编译一次,与导入次数无关。这意味着编译时间的显著改善。用户报告了3:
include <libgalil/DmcDevice.h> // 457440 lines after preprocessing
int main() { // 151268 non-blank lines Libgalil::DmcDevice("192.168.55.10"); // 1546 milliseconds to compile } 也就是说,编译近 50 万行代码只需要 1.5 秒。速度很快!但是,编译器的工作量太大了。
import libgalil; // 5 lines after preprocessing
int main() { // 4 non-blank lines Libgalil::DmcDevice("192.168.55.10"); // 62 milliseconds to compile } 这是 25 倍的加速。我们不能指望在所有情况下都能达到这个速度,但import比#include有 7 到 10 倍的优势是很常见的。如果你在 25 个源文件中#include了那个库。这将花费 1.5 秒,25 次,其中import总共需要 1.5 秒。
完整的标准库已被做成一个模块。看看传统的“hello world!”程序29:
include
int main() { std::cout << "Hello, World!\n"; } 在我的笔记本电脑上,编译时间为 0.87 秒。将#include<iostream.h>替换为import std;,编译时间下降到 0.08 秒,尽管提供的信息至少增加了 10 倍。
重新组织大量代码并不容易,也不便宜,但就模块而言,其在代码质量方面的好处是显著的,在编译时间方面的好处也是巨大的。
为什么我 — — 只在这个例子中 — — 费心解释“糟糕的旧方法”?因为#include无处不在,几乎从 C 诞生之日起就一直存在,许多开发人员很难想象没有它的 C++。
返回顶部
- 通用编程 泛型编程是当代 C++ 的关键基础。自“带类的 C”更名为“C++”之前,它一直如此,但直到最近(C++20),语言支持才接近理想状态。29
通用编程,即使用类型和类型参数化的函数进行编程,提供
更简洁、更易读的代码 更直接地表达想法 零开销抽象 类型安全 模板是 C++ 语言对泛型编程的支持,在标准库中随处可见:
容器和算法 并发支持:线程、锁…… 内存管理:分配器、资源句柄(例如向量和列表)、资源管理指针…… 输入/输出 字符串和正则表达式 还有更多 我们可以编写适用于所有合适参数类型的代码。例如,这是一个排序函数,它接受所有符合 ISO C++ 标准可排序范围定义的类型:
void sort(Sortable_range auto& r);
vector
array<int,128> ai; // … fill ai … sort(ai); 编译器有足够的信息来验证vs和ai的类型是否具有Sortable_range所需的内容;即,可以根据排序需要进行比较和交换类型的值的随机访问范围。如果参数不合适,编译器会在使用时捕获错误。例如:
list
5.1. 概念 概念是编译时谓词。即编译器要执行的函数,产生布尔值。它主要用于表达对模板参数的要求。概念通常由其他概念构建而成。例如,这是上述排序所需的Sortable_range:
template
概念可以采用一个或多个参数,并可从基本语言属性构建。要直接根据语言指定类型的属性(而不是根据其他概念),我们使用“使用模式” 6。例如:
template
通常,像在排序示例中一样,检查类型是否与概念匹配是隐式完成的,但我们也可以使用static_assert明确地完成:
static_assert(equality_comparable<int,double>); // succeeds
static_assert(equality_comparable
我们希望编写适用于所有合适参数类型的代码。但是,许多(可能是大多数)算法都采用多种模板参数类型。这意味着我们需要表达这些模板参数之间的关系。例如:
template
vector
我们一直都有概念。每个成功的通用库都有某种形式的概念:在设计师的头脑中、在文档中或在注释中。这些概念通常代表应用领域的基本概念。例如:
C/C++ 内置类型:算术和浮点35 C++ 标准库:迭代器、序列和容器 数学:单子、群、环、域 图:边和顶点、图、DAG…… C++20 没有引入概念的概念;它只是添加了概念的直接语言。概念是编译时谓词。使用概念比不使用概念更容易。然而,就像每个新奇的构造一样,我们必须学会有效地使用它们。
5.2. 编译时评估 概念是编译时函数的一个示例。在当代 C++ 中,任何足够简单的函数都可以在编译时进行求值:
constexpr:可以在编译时进行评估 consteval:必须在编译时进行评估 概念:在编译时评估,可以将类型作为参数 这适用于内置类型和用户定义类型。例如:
constexpr auto jul = weekday(December/24/2024); // Tuesday 为了允许consteval和constexpr函数和概念在编译时进行评估,它们不能
有副作用 访问非本地数据 有未定义行为(UB) 然而,他们可以使用广泛的设施,包括大部分标准库
因此,此类函数是纯函数概念的 C++ 版本,而当代 C++ 编译器包含几乎完整的 C++ 解释器。编译时求值也有利于提高性能。
返回顶部
- 指导方针和执行 当代风格带来巨大好处。然而,升级代码很难,而且通常成本高昂。我们如何才能使现有代码现代化?避免使用次优技术很困难。旧习惯很难改掉。熟悉常常被误认为简单。许多令人困惑和过时的信息在网络和教材中流传。此外,旧代码通常提供过时的界面风格,从而鼓励使用较旧的使用风格。我们需要帮助来指导我们更好的代码风格。
稳定性/兼容性是一个主要特性。此外,鉴于 C++ 代码有数十亿行,只有逐步采用新功能和技术才是可行的。因此,我们无法改变语言,但我们可以改变它的使用方式。人们(相当合理地)想要一个更简单的 C++,但也想要新功能,并坚持他们现有的代码必须继续运行。
为了帮助开发人员专注于有效使用当代 C++ 并避免该语言的过时“黑暗角落”,我们制定了一系列指南。在这里,我重点介绍我认为最雄心勃勃的 C++ 核心指南 [GC]。
一组指导方针必须代表与给定用途相关的语言的连贯哲学。我的主要目标是类型安全和资源安全的使用 ISO 标准 C++。也就是说
每个对象都按照其定义专门使用 无资源泄露 这包括人们所说的内存安全以及更多内容。这不是 C++ 的新目标24。显然,它不可能在每次使用 C++ 时都实现,但到目前为止,我们已有多年的经验表明,它可以在现代代码中实现,尽管到目前为止执行情况并不完善。
一套指南有优点也有缺点:
现已可用(例如,C++ 核心指南1) 个别规则可以执行也可以不执行 执法不完善 根据指导方针,我们需要执行:
个人资料是一套强制执行的连贯的指导方针规则1,30 正在 WG21 和其他31,10进行研究 除实验版本和部分版本16、7、15外,尚不可用 在思考 C++ 时,重要的是要记住 C++ 不仅仅是一种语言,而是一个由实现、库、工具、教学等组成的生态系统的一部分。特别是,使用 C++ 的开发人员所依赖的功能远远超出了 C 所提供的功能。
6.1. 指导原则 简单的 C++ 子集化不起作用:我们需要低级、棘手、接近硬件、容易出错且仅供专家使用的功能来高效实现高级功能并在需要时启用低级功能。C++ 核心指南使用一种称为超集子集25 的策略:
第一:用一些库抽象来扩展语言:使用标准库的部分内容并添加一个微型库,以便更方便、高效地使用指南(指南支持库,GSL)。 下一步:子集:禁止使用低级、低效且容易出错的功能。 我们得到的是“强化版 C++”:简单、安全、灵活、快速;而不是贫乏的子集或依赖大量运行时检查的东西。我们也不会创建具有新颖和/或不兼容功能的语言。结果是 100% ISO 标准 C++。混乱、危险、低级功能仍可在需要时启用和使用。
不同的应用领域有不同的需求,因此需要不同的指导方针,但最初的重点是“核心或 C++ 核心指导方针”。我们希望每个人最终都能从这些规则中受益
没有未初始化的变量 无范围或 nullptr 违规 无资源泄漏 无悬垂指针 无类型违规 无无效 有两本书按照这些准则描述了 C++,但错误除外:《C++ 之旅》面向经验丰富的程序员30,《编程:使用 C++ 的原则和实践》面向新手32。另外两本书探讨了 C++ 核心准则13,20的各个方面。
6.2 规则示例:不要对指针进行下标 指针不具备进行范围检查所需的相关信息。但是,范围检查对于内存安全和类型安全而言必不可少,因为我们不能允许应用程序代码读取或覆盖指向对象范围之外的对象。相反,我们必须使用具有足够信息进行范围检查的抽象,例如数组、向量或跨度。
考虑一种常见的样式:一个指针加上一个整数,该整数表示指向的元素的数量:
void f(int* p, int n) { for (int i = 0; i<n; i++) do_something_with(p[n]); } int a[100]; // … f(a,100); // OK? (depends on the meaning of n in the called function) f(a,1000); // likely disaster 这是一个非常简单的例子,使用数组来显示大小。由于大小存在,因此可以在调用点进行检查(尽管很少这样做),并且通常(指针,整数)对会通过较长的调用链,这使得验证变得困难或不可能。
解决这个问题的方法是将大小与指针紧密绑定(如Vector;§3.1)。这就是span的作用:
void f(span
核心指南支持库中引入了span类型,作为范围检查类型。遗憾的是,当将其添加到标准库时,范围检查的保证被删除了。显然,强制执行此规则的配置文件 (§6.4) 必须进行范围检查。每个主要的 C++ 实现都有方法确保这一点(例如,GCC 标准库强化15、Google Spatial 安全7和 Microsoft Visual Studio 的静态分析器16)。遗憾的是,目前还没有一种标准且可移植的方式来要求它。
6.3. 示例规则:不要使用无效的指针 某些容器(尤其是vector)可以重定位其元素。如果容器外部的某个人获取指向某个元素的指针并在重定位后使用它,则可能会发生灾难。请考虑:
void f(vector
这里不详细描述如何进行此分析。有关详细信息,请参阅27,8,32。但是,以下是该模型的概述:
这些规则适用于直接指向对象的每个实体,例如指针、资源管理指针、引用和指针容器。示例包括int、int&、vector<int>、unique_ptr
删除后禁止使用(显然)并依赖 RAII(§3)。 不允许指针脱离其指向的范围。这意味着,只有当指针指向静态内容、指向空闲存储空间(即堆和动态内存)或作为参数传入时,才能从函数返回指针。 假设一个函数(例如vector::push_back())接受非const参数时会失效。如果指向其某个元素的指针已被使用,我们将禁止对其的调用。仅接受const参数的函数不会失效,为了避免大量误报并保留本地分析,我们可以使用[[profiles::non_invalidating]]注释函数声明。当我们看到函数的定义时,可以验证此注释。因此,它是一个安全的注释,而不是“相信我”的注释。 当然,还有很多细节需要解决,但它们已经在实验以及目前正在实施的实施中进行了尝试。
6.4. 执行:个人资料 指南很好也很有用,但在大型代码库中始终如一地遵循它们基本上是不可能的。因此,执行至关重要。目前可以执行防止缺少初始化、范围错误、nullptr 取消引用和使用悬空指针的规则,并且已证明在大型代码库中是可以承受的16,7,15。
然而,关键的基础规则必须是标准化的(这是 C++ 定义的一部分),并采用标准的方式在代码中请求它们,以实现不同组织开发的代码在多个平台上运行和教学之间的互操作性。
我们将一套强制执行的、连贯一致的、提供保证的指导原则称为“配置文件”。根据目前为该标准规划的内容,初始配置文件集(基于多年来使用的核心指南配置文件)为10,1:
类型– 每个对象都已初始化;无强制类型转换;无联合 生命周期- 无法通过悬垂指针进行访问;检查指针取消引用是否为 nullptr;没有显式的new / delete 边界——所有订阅都经过范围检查;没有指针算法。 算术- 无溢出或下溢;无改变值的有符号/无符号转换 这本质上就是 §6.1 中描述的“核心中的核心”。随着时间和实验的进行,将会有更多的配置文件33。例如:
算法- 所有范围,没有end()迭代器的取消引用 并发性——消除死锁和数据竞争(很难做到) RAII - 句柄拥有的每个资源(不仅仅是用new / delete管理的资源)。 并非所有配置文件都符合 ISO 标准。我希望看到针对特定应用领域定义的配置文件,例如动画、飞行软件和科学计算。
执行主要是静态的(编译时),但一些重要的检查必须是运行时的(例如,下标和指针取消引用)。
必须明确请求翻译单元的配置文件。例如,
[[profile::enforce(type)]] // no casts or uninitialized objects in this TU 必要时,可以根据需要抑制语句(包括复合语句)的配置文件。例如:
[profile::suppress(lifetime))] this->succ = this->succ->succ; 抑制保证验证的需求主要是为了实现提供保证所需的抽象(例如span、vector和string_view)、保证范围检查和直接访问硬件。由于 C++ 需要直接操作硬件,因此我们不能将基本抽象的实现“外包”给其他语言。而且,由于其应用范围广泛且有多个独立实现,我们也不能简单地将所有基础抽象(例如,所有涉及链接结构的抽象)的实现留给编译器。
返回顶部
7.未来 我不愿意对未来做出预测,部分原因是这本身就很危险,尤其是因为 C++ 的定义由一个庞大的 ISO 标准委员会控制,该委员会以共识为基础。上次我检查时,成员名单有 527 人。这表明了热情、广泛的兴趣,并提供了广泛的专业知识,但它并不适用于编程语言设计,而且 ISO 规则不能进行重大修改。除其他主题外,正在进行的工作包括
异步计算的通用模型17 静态反射 [WC2014] SIMD 18 合同制度12 函数式编程风格模式匹配 [HSPM,PMPM] 通用单位制(例如 SI 单位制)20 所有这些的实验版本均已可用。
一个严重的问题是如何将不同的思想整合成一个连贯的整体。语言设计涉及在并非所有相关因素都已知的空间中做出决策,并且公认的结果几十年内都不会发生重大变化。这与大多数软件产品开发和大多数计算机科学学术追求不同。几十年来几乎所有的语言设计努力都失败了,这一事实表明了这个问题的严重性。
返回顶部
- 总结 C++ 的设计初衷是不断发展。刚开始的时候,我不仅没有资源来设计和实现我的理想语言,而且我也明白我需要使用反馈来将我的理想变成现实。C++ 在坚持其基本目标24 的同时不断发展。现代 C++ (C++23) 比任何早期版本都更接近理想,包括支持更好的代码质量、类型安全、表达能力、性能以及更广泛的应用领域。
然而,这种演进方法也带来了一些严重的问题。许多人对 C++ 的看法已经过时。如今,我们仍然看到无数人提到 C/C++ 这一神秘语言,通常认为 C++ 只是 C 的一个小扩展,包含了 C 的所有糟糕方面,并且严重滥用了复杂的 C++ 特性。其他消息来源将 C++ 描述为一次失败的 Java 设计尝试。此外,由于社区专注于较旧的使用方式,包管理和构建系统等领域的工具支持也落后了。
C++ 模型可以概括为
静态类型系统 对内置类型和用户定义类型的平等支持 值和引用语义 系统和通用资源管理 (RAII)高效的面向对象编程 灵活高效的泛型编程 编译时编程 直接使用机器和操作系统资源 通过库实现并发支持(由内部函数支持) C++语言和标准库是这一模型的具体体现,也是软件开发生态系统的重要组成部分。编程语言的价值在于其应用程序的质量。
返回顶部
- 参考文献
B. Stroustrup 和 H. Sutter (编辑):C++ 核心指南。
W. Childers 等人:C++26 的反射。WG21 P2996R6。2024 年。
D. Engert:C++ 模块(简短)导览。CppCon 2021。
D. Engert:当代 C++ 的实际应用。CppCon 2022。
GCC 异常性能。
G. Dos Reis 和 B. Stroustrup:指定 C++ 概念。POPL06。2006 年。
A. Rebert 等人:对数亿行 C++ 代码进行空间安全性改造。2024 年。
H. Sutter:终身安全:防止常见的悬垂。WG21 P1179。
H. Sutter:使用 is 和 as 进行模式匹配。WG P392R3。2024 年。
H. Sutter:C++26 的核心安全配置文件。WG21 D3081R1。2024 年。
P1179R1。2019-11-22。
J. Berne、T. Doumler 和 A. Krzemieński:C++ 契约。P2900R9。2024 年。
J. Davidson 和 K. Gregory:美丽的 C++:编写干净、安全和快速代码的 30 条核心准则。2021 年。ISBN 978-0137647842。
K. Estell:较小固件的 C++ 异常。CppCon 2024。
K.Varlamov 和 L Dionne:标准库强化。WG21 P3471R0。2024 年。
K. Reed:Visual Studio 2019 Preview 2 中的终身配置文件更新。2019 年。
L. Baker 等人:针对 C++26 的 std::execution 计划。WG21 P3109R0。
M. Kretz:std::simd — 数据并行类型。WG21 P21928R9。2024 年。
M. Park:模式匹配:匹配表达式。WG21 P2688R2。2024 年。
M. Pusz 等人:数量和单位库。WG21 P3045R2。2024 年。
R. Grimm:C++ 核心指南详解。Addison-Wesley。2022 年。ISBN 978-0136875673。
B. Stroustrup:类:C 语言的抽象数据类型工具。SIGPLAN 通告,1982 年 1 月。
B. Stroustrup: C++ 历史:1979-1991。ACM SIGPLAN。1993 年 3 月。
B. Stroustrup:C++ 的设计和演化。Addison Wesley,ISBN 0-201-54330-3。1994 年。
B. Stroustrup:语义增强库语言的基本原理。LCSD05。2005 年 10 月。
B. Stroustrup:在现实世界中发展一种语言:C++ 1991-2006。ACM SIGPLAN。2007 年 6 月。
B. Stroustrup、H. Sutter 和 G. Dos Reis:C++ 类型和资源安全模型简介。Isocpp.org。2015 年 10 月。2015 年 12 月修订。
B. Stroustrup:在拥挤多变的世界中蓬勃发展:C++ 2006–2020。ACM SIGPLAN。2020 年 6 月。
B. Stroustrup:标准库的最小模块支持。WG21 P2412r0。2021年。
B. Stroustrup:C++ 之旅(第 3 版)。Addison-Wesley。2022 年。ISBN 978-0-13-681648-5。
B. Stroustrup 和 G. Dos Reis:类型和资源安全 C++ 的设计替代方案。WG21 P2687R0。2022 年。
B. Stroustrup:编程:使用 C++ 的原理和实践。Addison-Wesley。2024.ISBN 978-0-13-830868-1。
B. Stroustrup:用于配置文件开发的框架。WG21 P3274R0。2024年。
B. Stroustrup:配置文件无效 - 消除悬空指针。WG21 P3346R0。
BW Kernighan 和 DM Ritchie:《C 编程语言》。Prentice-Hall。1978 年。ISBN 01-13-110163-3。


评论