import std; auto main() -> int { std::println("Hello world!"); } 随着 Modules 和 Formatting Library 顺利进入 C++20,标准库 Modules 和 Formatted output 进入 C++23 不过是水到渠成。这种先引入再扩展的标准发展方式在 C++ 中已不稀奇,这两个新特性自然也就成为 C++23 中主要的特性之二。 C++2a 之后,Ranges, Modules, Concepts, Coroutines, 以及将要引入的新特性,都将会在一定程度上改变我们以往的编译方式。而这一切的基础,都在于新特性是否足够完善。三年一小步,六年一大步,此之谓也。 由经典的入门代码,可以看到,C++俨然成为了一门新语言。越现代,越强大,这种使用方式以后可能会越发流行。因为不论是性能还是可用性,新特性都更具有优势。 首先是标准库 Modules。 Modules 具有缩短编译时间、解决重复替换、导入顺序无关,以及无需分离接口与实现等等优势,若标准库迟迟不提供 Modules 版本,反而会阻碍 Modules 的发展。因此,标准库Modules的优先级并不低,进标准也是顺理成章。 其次是Formatted output。 用过 fmt 库的都清楚,这可是个好东西,或者可以说是 cout 终结者。… Continue Reading “Hello World” in C++23

设计程序,经常需要分离不变的和变化的逻辑。将不变的逻辑放到一块,再以某种形式为变化的部分提供「定制点」,从而使程序具有更好的可扩展性,同时增加相似逻辑的可复用性。 因此,本质上来说,设计是为了应对变化。通过抽离系统的变化点,再以合适的结构来表示变化,从而控制变化的影响范围。 C++ 提供许多技术来表示定制点,大家最熟悉的就是 OOP 的继承和多态,而 Concepts 也是其中之一。那么 Concepts 为何可以表示定制点?它又产生了怎样的结果?这种方式有哪些好处?又有哪些缺点?与继承的方式有哪些区别?流程有何变化?实际例子有哪些? 这些问题,就是本篇将要讨论的内容。 首先,让我们来探讨一个问题:变化的最小单元是什么? 一个程序是一个系统,一个系统必须具备三个最基本的元素:输入、处理和输出。这三个元素都存在变化,不同的输入对应不同的处理,导出不同的结果。在编程语言中,输入和输出对应「数据」,处理对应「方法」。不同类型的数据需要不同的方法来进行处理,无数个数据和方法构成了整个程序。因此,变化就存在于数据与方法当中。 OOP 通过组块,将数据与方法组合起来,称为一个类,一个类就是一些具有联系的数据与方法的集合。但是,只有一个类无法应对变化,任何变化都会引起对该类的修改,或是重新编写相同的方法。因此,对需要共用某些相同数据或方法的一些类,进行向上一层的抽象,把这些相似的数据或方法放到更高层级,将其他变化的逻辑放到更低层级,使得低层级类可以使用高层级类的共有逻辑,从而提供扩展和复用的能力。通过这种结构化方式,将具备相似关系的类置于一个「层级体系」,高层级的类具有更高的抽象,低层级的类则更加具体。于是,高层级的类可以用来表示接口,低层级的类可以用来定制具体实现,以此来表示变化。「继承」便是用来表示层级关系,而「多态」则是用来重新定义方法。 Concepts是命名的约束,约束其实是一种条件关系,只有满足某些条件才能触发接下来的操作。OOP中,处于同一层级体系的类本身就是一种约束,对于其他类,由于不在该层级当中,因此不符合约束,也就无法使用那些共有的操作了。若要使用这些共有方法,则需要通过继承添加定制点。既然Concepts表示约束,那么就也可以作为一种表示定制点的方式。 那么具体来看,继承和多态是如何表示定制点的呢? 以一个简单的例子来说明: // Dynamic Polymorphism struct Graph { virtual ~Graph() = default; virtual void draw() const = 0; }; struct Circle : Graph { float radius; void draw() const override { std::cout << "draw… Continue Reading 使用Concepts表示变化「定制点」