CppMore
Dive deep into the C++ core, and discover more!
CppMore

岁月易得,不觉新凉似水,绿叶渐稀,来把形式逻辑继续写一写。 上篇讲了命题逻辑,能够用来处理命题之间的联结关系,对于简单推理已然足够。但当论证涉及个体、性质、关系以及“所有”“有些”等量化表达时,若把整个句子视为一个原子命题,便会丢失决定推理是否成立的关键信息。 一阶逻辑(First-Order Logic, FOL)由此而来。它保留命题逻辑中的否定、合取、析取、条件等逻辑联结词,并进一步引入个体、谓词、函数和量词,从而能够表达对象具有何种性质、对象之间存在何种关系,以及这些断言对哪些对象成立。 本文依次介绍一阶逻辑产生的必要性、形式语言、量词与变量、语义、逻辑关系与公式变换,以及自然语言的形式化方法。 Why First-Order Logic Limitations of Sentential Logic 看一个经典论证: 所有人都会死。 苏格拉底是人。 所以,苏格拉底会死。 在自然语言中,这个论证显然有效。 若只使用命题逻辑,可以令: $M$:所有人都会死; $S$:苏格拉底是人; $D$:苏格拉底会死。 于是论证被形式化为: $$ M,S \vDash D $$ 但这一语义蕴含并不成立。原因在于,在命题逻辑中,$M$、$S$、$D$ 只是三个彼此独立的原子命题。命题逻辑并不知道“苏格拉底”是一个个体,也不知道“是人”和“会死”是可以作用于个体的性质,更不知道第一个命题中的“人”和第二个命题中的“苏格拉底”之间存在实例化关系。 命题逻辑把整个句子压缩为一个不可再分的命题字母,丢失了句子的内部结构。真正决定上述论证有效性的,是以下结构: $$ \forall x(H(x)\to D(x)) $$ 表示任意对象 $x$,如果 $x$ 是人,那么… Continue Reading

本篇进入命题逻辑,英文常见的表达有 Sentential Logic 和 Propositional Logic,指的是一个东西。后续,命题逻辑也会简称为 SL。 Proposition 命题,是具有确定真值的陈述句。如: 北京是中国的首都。 他养了一只黑色的猫。 今天是星期一。 一般来讲,问句和祈使句无法判断真假,因此不是命题。如: 今天是星期几? 去把门关了。 命题逻辑处理命题之间的关系,以及由若干命题构成的复合命题和论证。 Arguments 论证是若干前提加一个结论组成的结构。通俗而言,就是一组句子,一些句子称为前提,还有一个句子称为结论。如: 前提一:明天要么下雨,要么下雪。 前提二:明天不会下雪。 结论:所以明天下雨。 结论必须基于前提,而前提支撑着结论。 将其形式化表示,就是用字母来表示句子。$P$ 表示“明天下雨”,$Q$ 表示“明天下雪”,则上述论证可表示为: 前提一:$P$ 或 $Q$. 前提二:非 $Q$. 结论:所以,$P$. 更进一步,将逻辑连接词用符号表示,便可表示为: $P \lor Q$ $\neg Q$ $\therefore P$ 因此,SL… Continue Reading

本篇开始,将有五到六篇的逻辑学内容,涉及逻辑知识框架、命题逻辑、量化逻辑、证明和应用。 为什么要写这个呢?在我的知识体系中,知识分为三层,L1 是元知识,L2 是基石知识,L3 是应用知识。编程属于 L3 知识,而逻辑学属于 L1 知识,更加底层,更加值得掌握。 GMP 库就是基于一阶逻辑演绎出来的,从 0 到 1 构建了一套宏元编程系统,能力甚强。纵然 C++ Generative Metaprogramming 前四章详细介绍了开发过程,也有许多人深感晦涩。可见,源码面前,并非了无秘密,代码终究只是表象,背后从无到有的设计思路才是根本,就像看小说一样,每个字都展示出来,你依旧无法从零创作一本。 L3 很难学到思考方式,其多为工具属性,下潜两层,方触根本。 第一篇,只会讲逻辑学的知识框架,算是帮大家快速建个路标。 什么是逻辑 逻辑,是前提与结论之间成立的推理关系。 如: 他是学生,要上课。 为什么这句话读着通顺呢?因为它符合逻辑。为什么符合逻辑呢?因为前提和结论之间有一条隐含的前提——学生要上课。 很简单,是吧?这只是一条逻辑链,如果将逻辑链拉长呢? 下雨了,他可能迟到。 这句话暗含三条逻辑链: 下雨了,所以地会湿。 地湿了,所以开车会慢。 开车慢,所以可能会迟到。 我们的大脑思考很快,所以读起来依旧通顺。但如果把逻辑链无限拉长呢?一百条、一千条、一万条,掐去中间,只留头尾,看起来就不那么显而易见了。 一个系统,从 0 到 1,亦是如此,看起来很复杂、神奇的功能,那只不过是因为隐去了过程。倘若之间没有无数条逻辑链支撑,便无法从 0 走到… Continue Reading

Introduction(引言) 我想构建一个基础知识的系列文章,以计算机学科为线索,系统地回顾一些重要学科的基础理论。 我们身处 AI 时代,得到的都是碎片化的内容残物,不知其从何而来,更不知其如何运用;我们也身处在一个被精心设计过的网络世界,人的喜怒哀乐成了提前设计好的公式,人的思想行动沦为有限空间的既定规则。 要想思路清晰,就要掌握基础知识,真正有用的知识并不难寻,就在于那些最基础、最不起眼的经典学科中。面对问题时,问题越普世,越可以从经典书籍中寻找解决方案,时间已经筛选出了最有效的答案。道的层面经久未变,术的层面日日更新,技术想做得长久,就不能只靠技术。 让我们开启这个新的旅程吧! Numbers, Quantities, and Numerals(数,数量和数字) 先从数开始。 现实中的多少,如三个人、五棵树、十袋粮食,就是数量。对数量进行抽象后得到的数学对象,如三个人、三杯水、三本书,共同特点是数量相同,于是被抽象为同一个数——3。用来书写数的符号,则称为数字,比如 10 可以表示为 10(十进制)、A(十六进制)、1010(二进制)。因此,数量存在于现实世界中,数是对此的抽象对象,数字则是表示数的符号。 但数在发展过程中,出现了许多现实世界之外的情况,所以要不断扩张数的概念,来兼容新的情况。 The Expansion of Number Systems(数列的扩张) 数的发展过程悠久,此间经常会出现之前的抽象对象无法表示的新情况,为了解决此类问题,就需要不断扩充数的抽象,同时兼容旧的抽象逻辑,于是数也有了类别。 Natural Numbers: Counting(自然数:计数) 数最早用来抽象表达现实世界中的物品数量,方便沟通时准确传递物品的多少,所以是用来解决计数这个实际问题的。这种数称为自然数,表示为: $$N=\left\{0,1,2,3,\dots\right\}$$ 这是一个可数的、无上界的无穷集合。 除了计数,自然数还可以表达先后情况,能够用来排序。 这里面最为特殊的是 0,在全球范围内,针对 0 是否属于自然数的争论依旧存在。认为自然数不包含零的其中一个理由是自然数所指为自然界中存在的数,例如一棵大树、两条鱼、十亿个细胞等等,而鲜少有人说零个物品。 在数论中,认为 0 不属于自然数,因此自然数会同义于正整数 $1,2,3,\dots$,而在集合论和计算机科学中,自然数指非负整数 $0,1,2,3,\dots$。数量为空,即没有一个东西,是一种无的表示。它首先是占位符,使 10、100、1005… Continue Reading

A quick recap Annotations 能够给代码结构附上一些额外信息,供 Reflection 随后查找。 直接上个例子: struct label { int value; }; inline constexpr label answer{42}; [[=answer]] int x; consteval label annotation_value() { auto annotations = std::meta::annotations_of(^^x); return std::meta::extract<label>(annotations[0]); } int main() { constexpr auto count = std::meta::annotations_of(^^x).size();… Continue Reading

前言 未来,国内城市的发展趋势呈现出一种 3+1+N 的大模式。3 指京津冀、江浙沪和粤港澳大湾区,1 指成渝经济圈,N 指武汉、长沙、郑州、合肥等城市。 这是“十四五”和“十五五”建议中皆提到并逐步明确的,今年是“十五五”规划的开局之年,从当前情况及政策规划上来看,国内未来的城市发展已然清晰。 从微观上来看,城市是个人为了利益最大化而产生的迁移行为所造成人口的空间集聚,人口的集聚是形成城市的基本条件之一。 一个城市,能否持续地把原本分散的人和机会压缩到一个空间里,降低交易成本,提高匹配效率,加快知识外溢,就是其相对于其他城市或乡村的核心优势。而如果一个城市不断发展壮大,它的这种优势会辐射到周围的城市,形成都市圈,都市圈不断发展,扩大辐射半径,形成大都市圈,最终形成城市群。 例如: 广州发展起来了,辐射佛山,就会形成一般的都市圈,都市圈同城化用来描述这两个城市可以像一座城市那样日常流动,具有便捷的通勤圈、梯次配套的产业圈和便利共享的生活圈。因此,跨城市间才会修建地铁。如今已经到了第四次发展阶段,体量需要更大,所以才一直在谈粤港澳大湾区城市群。 由上海辐射的苏州、无锡、常州、南通、宁波、嘉兴、湖州、舟山等城市,扩张的城市更多,所以属于第三阶段,称为上海大都市圈。大都市圈的下一阶段就是城市群一体化,也就是所说的长三角城市群。 长三角中心区和粤港澳大湾区也是我国初具世界级城市群规模和水平的两个巨型城市区域,世界级城市群是参与国际竞争与合作的重要平台。 城市发展,是政治力量在既定结构约束下组织经济活动的过程,目的是实现国家稳定。 一个城市的发展趋势,可以从三个层面进行分析:一要看客观条件,地理区位、自然环境、海陆位置、周边形势等先天结构决定了城市的基础势能;二要看组织选择,国家战略决定了城市的结构势能能否被激活;三要看价值实现,经济决定了政治推力能否让城市实现自增长、持续创造财富。 可以先从整体上看看各个省份的情况,对这些区域建立基本的印象。 先来看 2025 年各个省份的 GDP 情况。 【缺图】 2025 年,广东 GDP 达到了 145846.76 亿元,实际增速 3.9%,居全国第一。江苏紧随其后,达到了 142351.5 亿元,实际增速 5.3%。这两个省份的数据断崖式地领先其他省份,其他省份中只有山东刚刚突破 10 万亿大关,山东也是北方第一个 GDP 突破 10 万亿元的省份。 因为每个省份的人口数量不同,所以要看人均… Continue Reading

本篇回顾 Namespaces,特别常用的一个 C++ 特性。 Name Conflicts 随着项目模块变多,类型自然也会变多,此时就会遇到一个问题:多个模块间的类型出现重名。这就是名称碰撞问题,现有的策略主要分为两种思路。 第一种策略是从结构入手,为模块划分边界,作用域、命名空间、封装就属此类。这种策略需要更改程序的结构,增加新的语法。 第二种策略是从语义入手,建立共识,命名约定、增加前后缀就属此类。这种策略无需更改程序的结构,基于现有的语法就可以实现。 从效果上来看,第一种策略更加方便,能够更有逻辑地组织代码,协作开发大型项目,封装内部细节,支持带修饰的名称决议。 C++ Namespaces 就属于第一种策略,除了解决名称冲突,还可以用来划分程序的逻辑,控制 API 的表现形式。要编写一个成熟的大型项目,这属于必须掌握的特性之一。 Review of Namespaces C++ 存在三种形式的 Namesapces:Named Namespaces、Unnamed Namespaces 和 Nested Namespaces。示例: // Named Namespaces namespace A {} // C++11 inline namespace B {} // C++11… Continue Reading

问题 问题就是现实与期望之间的差距。 A 点为现状,B 点为目标,从 A 点迈向 B 点的过程就是解决问题的过程。 这是极简的问题定义,没有人不能理解。然而,真正遇到问题时,就很容易犯糊涂了。 例如: 明明现状是 A 点,许多人却误以为是 C、D、E 点,于是错误地朝着目标前进,实现目标了才发现那并不是自己真正想要的。 明明目标是 B 点,许多人却朝着 X、Y、Z 点努力前进,导致行动混乱,久无成效。 明明已经到达 B 点,许多人却继续投入资源和精力,反致弄巧成拙。 明明要朝着 B 点前进,许多人却迷失在过程里,忘了原本的目标。 …… 可见,要定位问题,也非一件易事。 看不见的敌人如何消灭?若连目标都无法分清,则行动犹难,遑论实现。 因此,定位问题远比解决问题重要。大多数问题都有现成的应对策略,问题定位了,也就解决一半了。 定位问题的错误分类 这里所说的定位问题有两层意思,一是确定目标,二是锁定目标。 从现实到期望的的途中,会遇到五种类型的错误:起点错误、终点错误、方向错误、路径错误和边界错误。 起点错误和终点错误属于未确定目标所引发的错误,方向错误、路径错误和边界错误属于未锁定目标所引发的错误。 按照解决问题的流程来说,起点错误和终点错误属于开始解决问题之前所产生的错误,方向错误和路径错误属于正式解决问题的过程中所产生的错误,而边界错误则属于解决问题之后所产生的错误。 起点错误 如果对现状的认识是错误的,那么由此推出的目标和路径也必然是错误的。这就是起点错误。 例如一个人感觉自己不幸福(对 A… Continue Reading

本期主题:Static Reflection 讨论时间:2025-07-20 20:00/22:30 记录时间:2025-07-24/26 问题总数:33 主持人:里缪 参与者:殇心哈、keno、逐月、枣树、Lin、安知鱼之乐、Yolo、蓝芝熊、chan、bender、ZIRQ、哼哩🦈、毛毛、Frank、冲浪的小鱼、平安是福、佐、tearshark、David(共 19 人) 前言 烈日炎炎,芭蕉冉冉,大家的探索之心难断。 作为 C++ 最新的强大特性之一,静态反射带来了诸多新玩法。本次即以此作点,发散思考,提出问题,围而攻之。 有哪些概念?哪些迷思?哪些疑惑?哪些思考?哪些启发? 且看本期讨论内容。 C++ 静态反射与其他语言(如 Java、Go、C#等)的反射有哪些核心区别? 核心区别在于反射时期,反射有编译期和运行期之分。 对比如表: 语言 反射时期 运行时开销? 运行时环境 Java 运行期 有 JVM C#(.Net) 运行期 有 CLR Python 运行期 有 解释器(如 CPython) Go… Continue Reading

引言 盛夏到了,赤日当空,蝉吟鹊噪,绿树浓阴如盖,万物争展其生机。 静态反射方冒出头,便给 C++ 注入了久违的活力。这是一个全新的语言。新在哪里呢?当然不是反射本身,而是伴随反射而来的代码生成能力。从宏元编程,到模板元编程,再到如今的反射元编程,岁月苒苒,大家会认识到这是 C++ 新纪元的开始。 C++26 静态反射通过 Spec API 注入模型来提供反射元编程能力,尽管这只是一种略显笨重的注入机制,不如 Fragments、Token Sequences 等注入模型灵活,但至少当前就能使用,能够初步实现反射元编程了。 静态反射早在 CGP 一书的最后两章写过,变化实轻,便不絮繁。本文只回顾并补充注入模型的相关内容。 Spec API 注入模型 C++26 的 Spec API 注入模型包含两个元函数:define_aggregate() 和 data_member_spec()。声明如下: namespace std::meta { struct data_member_options { struct name_type { template <typename T>… Continue Reading

去年,我在“What does the …… mean in C++?”一文中专门介绍过 ...... 这种极其罕见的 C++ 语法,它其实是 ..., ... 的省略语法。以下这三种写法完全等价: template<class... Args> void f(Args......) {} template<class... Args> void f(Args... ...) {} template<class... Args> void f(Args..., ...) {} 第一个 ... 用于扩展 C++ 可变模板参数包,而第二个 ... 用于匹配 C 语言中… Continue Reading

C++17 存在一个不起眼的类型 std::monostate,引入背景是 std::variant 需要允许默认构造,而如果它所有的候选类型都不支持默认构造,那就可以将 std::monostate 作为第一个候选类型来避免无效状态。 一个小例子: struct S { explicit S(int) {} }; // Error: the first alternative is not default constructible. std::variant<S> v; // OK std::variant<std::monostate, S> w; 因此,std::monostate 也被放到了 <variant>,定义很简单: struct monostate {}; 许多人容易把它当成是一个“空”类型,但从严格意义上来讲,它同 void 一样,是一个单值类型。… Continue Reading

std::projected 是 C++20 引入的一个模板工具,用于获取应用投影函数后迭代器所指向的值类型。例如: struct Person { std::string name; int age; }; int main() { std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}}; auto proj = [](const Person& p) { return p.age; }; using ProjAge = std::projected<std::vector<Person>::iterator, decltype(proj)>; static_assert(std::is_same_v<ProjAge::value_type,… Continue Reading