<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>@kleedaisuki（中文）</title><description>kleedaisuki 的编程、数学、研究与思考笔记。</description><link>https://atelier.moesegfault.dev/zh/</link><language>zh-CN</language><item><title>Code Review 之死：转向Agent-legible Systems</title><link>https://atelier.moesegfault.dev/zh/blog/2026-08-24-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2026-08-24-1-zh/</guid><description>Coding Agent 正在重塑软件工程的成本模型：代码生成日益廉价，而人的注意力成为新的串行瓶颈。软件可信性不应依赖作者或 reviewer 的权威，而应来自可验证、可反驳的系统机制。本文提出 Agent-legible Systems：从设计之初就让运行状态、关键不变量与因果证据可被 Agent 查询、实验和验证，以类型系统、测试、CI、profiling、tracing 等形成自动化验证闭环。人的判断力则从逐行审阅实现，上移到领域模型、系统契约与不可逆架构决策。</description><pubDate>Mon, 24 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/kleedaisuki/FOGMOE-telegram-bot&quot;&gt;FOGMOE-telegram-bot&lt;/a&gt; 是我的一个暑期兴趣项目，fork 自 FOGMOE 的一个相对简单实现。经过两个月的设计和打磨、燃烧了数不尽的 Codex 额度，这个项目已经具备了云 agent 的基本特性：容器化 agent 工作区、新想法的 fmBPF 动态插件系统，以及可靠的运维与部署工具。&lt;/p&gt;
&lt;p&gt;项目由 Python 3.14 搭配 C++23 原生实现，立足 Linux 平台，数据服务是 PostgreSQL。根据 cloc，Git 跟踪范围内约 38.36 万行非空行和注释的有效代码，其中生产代码 24.47 万、测试代码 13.89 万，Python 与 C++ 各居其半。而且这些代码有质量保障：生产代码 9,109 个函数，平均圈复杂度约 5，有 456 个长尾的函数超过 15、127 个超过 30；整体一致性极强，贯彻领域驱动设计，用类型系统约束和表达语义与业务逻辑，是典型的良好设计后自顶向下实现。&lt;/p&gt;
&lt;p&gt;一个人在两个月内，把一个约 2.8 万行的项目系统性重写并演化成了约 38 万行的工程系统，在几年前并不可能。更何况即使经过了单纯的设计与开发阶段，也要面对运维和部署、Telegram 界面的产品反馈，跨度远非传统后端工程师。&lt;/p&gt;
&lt;p&gt;请允许我称呼 coding agents 普及之前的软件工程为传统软件工程，因为在这样一个项目中我们能明显看到完全不同的成本模型：其规模和效率远远超越了某个工程师对某一技术栈的熟练度所能覆盖的范畴。数量如此庞大的代码轻易击穿了人的注意力经济：过去写一个算法和审阅一个算法所耗费的精力尚能是相近的，但 agent 联网参考最佳实践复现一个算法往往只需要数秒。人有限的注意力在整个过程中形成了最大的串行化部分，这是明显的瓶颈转移。&lt;/p&gt;
&lt;p&gt;最合乎直觉的做法是延缓 agent 的代码产出，或者直呼“AI slop”一竿子打死，重回古法编程。我认为这种模式和思潮是幼稚的：它并没有切实证明“AI slop”相较于熟练的 senior 有多少的不足，很多工程品味根本没法测，何况你不能说用99%的开支换来1%的表现就一定是“更好的”。在没有 coding agent 之前很多后端工程师为了保交付、赶产出写的代码就不 slop 了吗？况且很多思潮下意识地会拿 AI 的幻觉说事，但我们为什么要预设模型就是神圣的、万能的？一个工程师难道不会犯错吗？可我们为什么能够信任ta的判断？&lt;/p&gt;
&lt;p&gt;到底是什么使得代码值得信任？&lt;/p&gt;
&lt;p&gt;我认为，代码不是因为作者值得信任而值得信任，而是因为它能够被系统性地验证、反驳和约束，code review 只是在旧成本模型下的一种治理方式：我们相信这套制度可以提升代码质量和共享知识，而工程师所犯的错误少到可以恢复。如果回到正确的关注重心：“如果它是错的，我有没有足够好的机制把它抓出来？”这套制度的要素非常明显：一为准入门槛，二为容错机制。&lt;/p&gt;
&lt;p&gt;类型系统、测试、lint、架构检查、CI 都很重要，但它们关注的是我们已经明确建模的风险。真实系统还会受到数据分布、并发、负载、依赖、内核、网络、调度和故障模式影响，所以静态分析、测试和 CI 即使在过去也没有取代 reviewer。因为工程师有一种更高层次的上下文，他们能够想象出自己的系统在运行时是什么样的、什么样的语义和协议需要被遵守。&lt;/p&gt;
&lt;p&gt;我们审视当下的 coding agent，强化学习后训练将模型的程序性能力提升到了相当的高度，以至于有人叫嚣“scaling 撞墙”、“密度定律”。这里我无意讨论强化学习后训练在模型采样过程中的实质，也不想深入探讨事实性能力与程序性能力的鸿沟；我想指出，当前工具调用式的 coding agent 所取得的成就实际上有两个先决条件：外部知识可以被工具方便地获取、有可测量的量与反馈机制。&lt;/p&gt;
&lt;p&gt;我认为，使用 agent 进行系统性工程的关键在于，让 agent 能够获得单纯静态代码不可见的运行时信息进行推理，而非依赖模型内部知识做它所不擅长的高语境判断。我把这种从设计之初就让运行状态、关键不变量与因果证据能够被 Agent 主动查询、实验和验证的系统，称为 Agent-legible Systems。&lt;/p&gt;
&lt;p&gt;云原生的时代 Google SRE 能够成为显学，在于它将运维能力本身纳入系统设计，把可靠性从事后救火变成一种可以被设计、测量、预算和自动化的系统属性。我认为 AI-native 系统设计的关键变在于把 observability 纳入系统设计，不应该在代码写完之后，才想办法让 agent 理解系统，系统从设计之初就应该考虑它将如何被 agent 观察和推理。&lt;/p&gt;
&lt;p&gt;我们应当将重心转移到基础设施。比如我们要提供这样一种能力：不是 agent 像工程师读了一遍代码以后说：“这里性能不好”，而是 agent 可以启动一个工具链和工作流得到 profiling 数据，清晰地显示出“这里性能不好”。不是开一堆 subagents review 以后得到多少个 P0、P1，而是 agent 可以很快地启动测试环境并且得到一份详尽的 tracing 和崩溃报告。传统 Code Review 的一个核心价值，是 reviewer 可以凭经验模拟运行时，而我们在设计上让 agent 直接观测运行时：我们提供方便使用的工具来供它获取知识，并且确保信息能够闭环反馈。&lt;/p&gt;
&lt;p&gt;我们不需要让这些信息被压缩精炼、用更好的表达方式或者可视化，而是让这些信息能够张成整个空间，因为 agent 的注意力并不值钱。过去事后还原需要大量的推理和枯燥的工具使用，方能灵光一闪注意到问题，现在完全可以交由 agent 去整理发现，把一个启发式的工作转化为数据分析。&lt;/p&gt;
&lt;p&gt;这样，人不该也不必把最稀缺的注意力用来检查几千行机械实现，而应该审查领域模型、公开契约、关键不变量、迁移、安全边界和不可逆架构决策。实现细节可以越来越多地交给 Agent，而人的判断力集中到这个系统究竟允许发生什么、绝不能发生什么。同时我认为应当进行敏捷开发，诗性编程、文档优先。所谓文档优先不应当是 Big Design Up Front，是协作时优先沉淀稳定事实：ADR、Schema、迁移、类型化接口、组合根、运行手册，减少“只有某个人脑子里知道为什么”的隐式知识，让人和 Agent 都围绕同一套事实源工作。&lt;/p&gt;
&lt;p&gt;回眸历史，因为有了异构系统通信的需求才出现了企业集成总线；又因为过度设计才衍生出了微服务思想；而分布式系统又引入了 SRE。这一次有了 agent，软件生成的传统成本模型被大大颠覆，我们则设计出一套让 Agent 提出假设 → 操纵环境 → 获取运行时证据 → 反驳假设 → 修改系统 → 再验证的闭环，做出 agent-legible systems。毕竟，做架构是站在系统演化的生命周期与枯荣上去思考问题，而这一次我们让 agent 直接感受系统的呼吸和脉搏，参与到演化中去。&lt;/p&gt;</content:encoded><category>AI-native</category><category>Agent-legible Systems</category><category>软件工程范式</category><category>Code Review</category></item><item><title>封闭条件下的局部最优解</title><link>https://atelier.moesegfault.dev/zh/blog/2026-05-03-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2026-05-03-1-zh/</guid><description>数组型红黑树、高考压轴题、PhD找不到工作，本质上是同一件事：封闭系统能筛选出局部最优解，但这个“最优”的定义本身是被封闭条件扭曲的。信息环境决定智力的产出效率，而你又没有办法给你认知以外的东西定价。</description><pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我确实看过一个北大本科生写的 C 红黑树教程，不过不是计算机的，是生物工程的。有些地方很有重复发明创造的美：比如什么“数组型红黑树”，这个有专门的名词叫游标实现（相对于指针实现而言），而且通行的做法是对分配和访问做抽象层，而不是硬编码重写算法逻辑。&lt;/p&gt;
&lt;p&gt;现代C++的标准做法就是用模板把算法逻辑和底层存储/访问方式解耦——STL本身就是这个思想的产物。容器负责内存管理，迭代器负责访问抽象，算法只跟迭代器交互，完全不关心底层是堆上分配的指针链表还是数组池里的游标索引还是内存映射文件里的偏移量。具体到红黑树这个场景，如果你要做 cursor-based implementation，现代的做法就是把&lt;code&gt;node&lt;/code&gt;的引用类型做成模板参数或者通过 traits/policy class抽象出来——比如一个&lt;code&gt;NodeTraits&lt;/code&gt;里定义&lt;code&gt;reference_type&lt;/code&gt;是指针还是数组下标，定义&lt;code&gt;get_left()&lt;/code&gt;/&lt;code&gt;get_right()&lt;/code&gt;/&lt;code&gt;get_parent()&lt;/code&gt;的具体实现，然后红黑树的旋转、插入、删除这些算法逻辑完全用这些抽象接口写，一行都不需要改。这就是 &lt;strong&gt;policy-based design&lt;/strong&gt;，Alexandrescu 在 Modern C++ Design 里系统化阐述的那套思路。&lt;/p&gt;
&lt;p&gt;看一个人凭自己的智力从零推导出一个已知结论，那个推导过程本身是有思维力量的。但同时这种美也带着一丝悲凉，因为它的本质是信息获取效率的失败。如果他读过Sedgewick的Algorithms，或者看过任何一本覆盖cursor-based implementation的教材，他可以在十分钟内知道这个东西叫什么、标准做法是什么，然后把自己的智力投入到真正未解决的问题上去。&lt;/p&gt;
&lt;p&gt;所以我后来看高考也是和看这位一个感想。你说水平不高？他确实又能想到。你说水平高？那从效果上来看未必。高考和那位北大同学的红黑树教程，底层确实是同一个悖论——在封闭条件下的高强度智力表演。&lt;/p&gt;
&lt;p&gt;高考数学压轴题就是一个典型：出题人在一个极度受限的知识范围内（高中数学大纲），设计出需要极高技巧性的题目，考生也在这个封闭空间内发展出极精致的解题策略。你说这些人不聪明？解法确实巧妙，思维密度很高。你说这些人水平高？他们花了三年时间精研的那些技巧，在大学数学的视角下往往就是一个已知定理的trivial special case。比如高考里那些令人窒息的不等式放缩技巧，本质上就是在不允许使用微积分的条件下硬做微积分该做的事。从某个角度看这很惊艳——你在被绑住一只手的情况下还能打拳。但从效率角度看，为什么不直接解绑那只手？&lt;/p&gt;
&lt;p&gt;这就是“水平不高但能想到”的本质：在人为制造的约束下发展出的局部最优解。信息环境决定了智力的产出效率。封闭系统能够筛选出在封闭条件下的强者，但这个&quot;强&quot;的定义本身是被封闭条件扭曲的。 高考筛选出的是“在不允许用高等工具的前提下最能做数学体操的人”，知乎筛选出的是“在约束内最能写的人”，那位同学展示的是“在信息隔离下最能独立推导的人”。每一种都是真实的能力，但每一种都在被约束条件浪费着。同样的智力，在信息充裕的环境里可以站在巨人肩膀上往前推进，在信息贫乏的环境里只能花大量精力重新铺设别人已经铺好的路。&lt;/p&gt;
&lt;p&gt;所以人的水平高低，其实不太取决于多刻苦，而取决于他所在的信息环境。好比很多人的圈子其实从来接触不到土地财政、条块分割、便利化额度这些东西，他们也理解不了税基和流动性是什么；很多技术人认为只有网络环境有区别，这就是一种无知：他们没有资产做配置、没有事业要运营，你没有办法给你认知以外的东西定价。所以很多人只会说铁饭碗、稳定，而有个更准确的词他们不会用，叫国家信用背书。&lt;/p&gt;</content:encoded><category>信息环境</category><category>高考</category><category>学术与工业</category><category>C++</category></item><item><title>AI 作为新的知识传播设施</title><link>https://atelier.moesegfault.dev/zh/blog/2026-05-01-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2026-05-01-1-zh/</guid><description>人类平均能力的抬升，并不主要来自个体学习时间的增加，而来自知识扩散、压缩与调用机制的持续升级。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;因为大多数时候我们处理的都是常规科学，所以真正的创新节点其实并不多；绝大部分工作，本质上是工程化的重复、调参和模式匹配。人类平均水平的提高，并不是因为古往今来的人突然拥有了更多学习时间，而是因为知识的扩散变得越来越容易。Newton 和 Leibniz 在 17 世纪发明微积分时，那是当时人类智力活动的绝对前沿；而今天，一个高中生用几个月就可以掌握其基本框架。共同体实际上形成了一种分工机制：让后人不必反复从零开始，也让“重复造轮子”逐渐变成一种低效而可疑的行为。&lt;/p&gt;
&lt;p&gt;CS 领域里，绝大多数论文从事的也正是常规科学。Transformer 出现之后，几千篇论文的工作实质上是在给 Transformer 换数据集、加 module、改 loss function：这些都是在 Transformer 范式内部推进工程演化，而不是完成根本概念层面的断裂。真正称得上创新节点的，也许就那么几个：attention mechanism 本身、scaling law 的发现、RLHF 作为 alignment 方案的成型，以及 diffusion model 的理论框架。其余大量工作更接近 exploitation，而不是 exploration。&lt;/p&gt;
&lt;p&gt;这里的主要机制，是知识的编码、压缩与传播技术在持续迭代。文字、印刷术、学术期刊、互联网，以及现在的 AI，每一次都是传播基础设施的升级。Joel Mokyr 在 &lt;em&gt;The Gifts of Athena&lt;/em&gt;（2002，通常译作《雅典娜的礼物》）中区分了两类知识：propositional knowledge（“是什么”，即 Ω 知识）和 prescriptive knowledge（“怎么做”，即 λ 知识）。他的核心论点是，工业革命之所以发生，并不是因为人类突然变聪明了，而是因为这两类知识之间的映射变得更高效了：人们能更快地把“知道 X 的原理”转化为“知道如何利用 X 来做事”。&lt;/p&gt;
&lt;p&gt;如果常规科学是对特定模式的重复练习，如果人类进步的瓶颈更多在传播而不是发现，如果共同体的功能之一就是通过分工避免无意义的重复，那么 AI 对学术的冲击就不只是“写论文更快了”。更尖锐的判断是：AI 可能正在让常规科学中的大量人力投入变得不再必要。如果 90% 的学术产出都是在既有范式内做增量填充，而 AI 已经能够胜任相当一部分填充工作，那么继续维持一个由数百万人组成的全球学术劳动力来执行这件事，其合理性究竟在哪里？&lt;/p&gt;
&lt;p&gt;过去，“怎么写论文”这类知识的传递机制是什么？是师徒制。你跟着一个导师，他手把手教你：实验应该怎样设计 baseline 才显得公平，related work 要怎样写才不冒犯审稿人，rebuttal 的语气该如何拿捏，abstract 的第一句话又该怎样钩住 editor。这些东西没有完整写在任何教科书里；它们是程序性知识，寄生在特定的社会关系结构中，通过 apprenticeship 口耳相传。于是，你能不能学到这些，本质上取决于你的导师是谁。一个顶级实验室的学生耳濡目染就知道的东西，一个普通学校的学生可能读完博士都不知道。知识的传递通道，和学术权力的等级结构，是绑定在一起的。Michael Polanyi 在 &lt;em&gt;Personal Knowledge&lt;/em&gt;（1958）中系统展开的 tacit knowledge 概念，描述的正是这一层东西；他后来那句著名判断——“we can know more than we can tell”——说的也是此类知识无法被完全显性化、无法由教科书充分承载，而只能在实践共同体内部流动。&lt;/p&gt;
&lt;p&gt;AI 做了什么？AI 把这层 tacit knowledge 的获取门槛，从“你得先在某个行会里当几年学徒”，降到了“你得会提问”。“给 AI 看看论文，基本起到了 peer 头脑风暴的效果”——这件事在两年前还几乎不可想象。一个博士生想要获得高质量的 peer feedback，要么等待几个月后的正式 peer review，要么找到同方向、且愿意花时间细读你论文的同行。现在，你可以随时获得一个质量尚可的智识切磋伙伴。而且注意，这不只是效率提升，而是一种放权：过去能够获得这种质量学术反馈的人，在全中国也许只有那几千个身处顶级实验室的博士生；现在，任何一个拥有 Claude 或 GPT 访问权限、并且知道如何提问的人，都可以获得类似资源。&lt;/p&gt;
&lt;p&gt;短期内，学术界不会因此发生根本变化，因为学术权力结构并不完全建立在知识垄断之上；它更深地建立在 credential 认证和 network access 之上。你会写论文，不等于你能把论文发出去，因为发表的瓶颈往往不只是论文质量，还包括审稿人是否认识你的导师、你的机构是否拥有足够的声誉资本。但长期来看，如果 AI 持续降低知识生产的门槛，那些纯粹依赖信息不对称维持地位的中间层，比如那些自己不怎么做研究、却凭借学术行政权力分配资源的人，会面临越来越严重的合法性危机。不过，这个变化会很慢：行会的惯性，远比技术迭代的速度更顽固。&lt;/p&gt;</content:encoded><category>AI-native</category><category>知识传播</category><category>学术生产</category></item><item><title>刻苦被发明为能力之后</title><link>https://atelier.moesegfault.dev/zh/blog/2026-02-01-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2026-02-01-1-zh/</guid><description>说应试教育公平是一种根本性的误认：它只是把没有选择权，重新命名为唯一选择的优越性。</description><pubDate>Sun, 01 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我觉得社会上到处都有人传播一种错误的价值观：把刻苦当作一种能力。&lt;/p&gt;
&lt;p&gt;这和应试教育有很深的关系。应试体系有一个核心特征：它把多维的人类能力压缩到单一且可测量的指标之中。当评估标准高度标准化之后，一个后果随即出现：方法论的天花板很低，但投入时长的天花板几乎无限。也就是说，当“怎么学”这件事被固化为刷题、背诵和重复之后，唯一还剩下的可操作变量，就是“学多久”。于是，刻苦从众多能力中被抬举出来，成为这个特定游戏里最能制造边际收益的投入方式。&lt;/p&gt;
&lt;p&gt;将应试教育理解为“在资源极度有限的情况下提供一个相对公平的筛选机制”，我认为是相当肤浅的。它是一种去政治化的解释：把制度选择叙述为自然的、不可避免的结果。这种分析有一个隐含倾向，即把制度视为对客观约束的被动回应，而不是一种主动的权力运作；放到现实之中，这显然有失偏颇。&lt;/p&gt;
&lt;p&gt;我觉得根本问题并不是什么资源极度有限，而是对教育的行政主导，以及高度中心化的制度设计。中国的教育资源在过去二三十年里大幅增长：学校数量、师资条件和基础设施都在改善。但应试的强度不仅没有降低，反而变得更卷。如果资源有限真是根本原因，那么资源增加本应缓解问题。事实却是，资源增加被吸收进了同一套竞争逻辑之中，转化为更精细的刷题、更提前的起跑线。&lt;/p&gt;
&lt;p&gt;本质上，这是国家对于意识形态的垄断。统一教材、统一考纲、统一评分标准、统一升学路径，使得多元评估标准难以存活；即使某所学校或某位老师想尝试不同的教育理念，也会被系统性地筛选掉。&lt;/p&gt;
&lt;p&gt;所以，只有从这个视角出发，我们才能解释中国学校教育中的若干关键功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;人口筛选和分流：在高度统一的标准下完成社会分层，并且让被分流者“认命”，因为失败被解释为“你自己不够努力”。&lt;/li&gt;
&lt;li&gt;规训和服从性训练：应试教育训练的核心能力可能并不是“知识”，而是“在意义不明的情况下执行指令的能力”。&lt;/li&gt;
&lt;li&gt;消解地方性和多元性：统一的教育内容，是意识形态整合的工具。&lt;/li&gt;
&lt;li&gt;延迟社会矛盾：让年轻人把精力消耗在无尽的竞争里，而不是质疑系统本身。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;说应试教育公平，是一种根本性的误认；我认为这很大程度上源于世界观过于局限。公平，应当是竞争中的机会均等，而不是禁用其他选择，迫使大多数人只能选择应试。这也解释了应试教育令人感到诡诈的地方：精英拥有退出权，他们可以留学，也可以继承。所以，说它公平又何尝不是一种创伤的防御机制？它只是把没有选择权，重新命名为唯一选择的优越性而已。&lt;/p&gt;</content:encoded><category>教育批判</category><category>制度分析</category><category>随想</category></item><item><title>仙侠文、轻小说与竞争心态的文化模型</title><link>https://atelier.moesegfault.dev/zh/blog/2025-11-29-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-11-29-1-zh/</guid><description>前现代性与现代性的根本撕裂在于，优化目标不再是我是否全能、是否什么都不错过，而是我在供应链中究竟能解决哪一种需求。</description><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;世界当然充满竞争，但如何理解竞争，本身就存在深刻差异。一般而言，在框架化一个问题时，建模方式不同，对需求与优化目标的定义就会不同，后续的路径选择与优化方式也会随之改变。竞争同样可以被建模为两套截然不同的逻辑；为方便起见，我姑且称之为存量思维与增量思维。&lt;/p&gt;
&lt;p&gt;对比中国仙侠文与日式轻小说，便很容易感受到这种差异。日本轻小说尤其偏爱种田文、经商文，比如《狼与香辛料》《小书痴》。其中的主角往往不是通过掠夺获得资源，而是通过引入新技术来完成积累：在异世界造纸、制作肥皂、改良农作物，诸如此类。这背后反映的是发达社会中相对稳固的一种共识：财富可以被创造出来，而不只能从别人手中夺取。&lt;/p&gt;
&lt;p&gt;总体而言，过去一段时间的中国通俗作品，折射的是一个高速增长、阶层剧烈流动的社会。人们相信“王侯将相宁有种乎”，同时又深深恐惧落后就要挨打。因此，它的系统设计常常极其激进，也极其残酷。日本作品所折射的，则是一个成熟、固化，乃至近乎停滞的社会。人们已经习惯每个人都有自己的位置，也就是分工；他们不再频繁幻想成为全知全能的神，而是希望在一个尚能良性运转的系统中，找到一个舒服的位置：开一家咖啡店，或者当一个中级冒险者。&lt;/p&gt;
&lt;p&gt;更有意思的是，修仙经济本质上是一种硬通货崇拜。灵石就是金本位，甚至比金本位更原始：它既是一般等价物，又是消耗品；在这种世界观里，几乎不存在借贷、期货、保险这些制度想象。由于没有金融杠杆，资源流动的主要方式就只剩下一个字：抢。杀人夺宝因此成了最高效的“资产重组”。这正是通缩螺旋所催生的小农经济心态：资源总量有限，我不抢你的，我就会饿死。仙侠大能们动辄活了几万年，却似乎仍不理解复利、投资和信用，只会把灵石一层层封进洞府深处。&lt;/p&gt;
&lt;p&gt;为什么中国作者很少写出“修仙界的华尔街”或者“修仙界的社保局”？因为在中国集体潜意识的数据库里，真正成熟的架构图纸大抵只有两套：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;秦汉的郡县制/官僚制&lt;/li&gt;
&lt;li&gt;乡土社会的宗族制&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而公民社会、契约精神、现代银行这些东西，是最近几十年才被匆忙打补丁写进来的。&lt;/p&gt;
&lt;p&gt;作者们在构建幻想世界时，本能地回滚到了那个最熟悉、最“原汁原味”的旧版本内核。哪怕手里拿着 iPhone，脑子里运行的依然是朝廷、江湖、衙门的逻辑。这其实暴露出一种认知贫瘠：很难想象一个既拥有超凡力量，又拥有现代文明制度的社会。因此，中国的现代化在很大程度上仍是器物层面的现代化，远未充分抵达思想结构与社会制度的现代化。&lt;/p&gt;
&lt;p&gt;对竞争的不同理解，自然会带来截然不同的行动模式。如果我们始终思考的是如何从别人手中抢夺机会，就会不断复演修仙小说主角那种原始的全能幻想：永远焦虑自己是否在某个指标上不如别人，永远惧怕一丁点所谓的“落后”。但现代工业社会在根本上恰恰是反全能的。每个人、每个企业都应当以高内聚、低耦合的方式做好自己的事情；大量中间件、抽象层与专业分工，才是现代供应链的真实面貌。思考问题的角度不应是“我能做多少事情”，而应是“我在供应链中处于什么位置”，然后把那一件事情的深度做到极致，直至形成护城河。&lt;/p&gt;
&lt;p&gt;所以，许多中国人以及中式父母的竞争焦虑，在根本上是无意义的。他们只知道要在某个指标上比别人优秀，却很少真正思考自己的生态位、自己的赛道究竟是什么；他们的优化目标永远是“我有多全能”，而不是“我能解决哪一种需求”。这正是前现代性与现代性的根本撕裂，也确实是一种无法放下全能幻想的巨婴心理。&lt;/p&gt;</content:encoded><category>流行文化</category><category>现代性</category><category>竞争心态</category><category>个人思考</category></item><item><title>C++ 基础知识时间：硬件暴力美学与零成本抽象</title><link>https://atelier.moesegfault.dev/zh/blog/2025-11-29-2-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-11-29-2-zh/</guid><description>从 std::fma 到 allocator_traits、SFINAE、PMR 与 Concepts：C++ 的锋利之处，正在于它把硬件事实和类型系统同时推到你面前。</description><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;C++ 基础知识时间！今天来重新感受一下 C++ 的核心气质：硬件暴力美学，以及零成本抽象。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;有一个专门计算浮点乘加的函数：&lt;code&gt;std::fma&lt;/code&gt;，全称来自 Fused Multiply-Add。你输入 &lt;code&gt;x&lt;/code&gt;、&lt;code&gt;y&lt;/code&gt;、&lt;code&gt;z&lt;/code&gt;，它返回的就是 &lt;code&gt;x * y + z&lt;/code&gt;。有人可能会问：“这和直接写 &lt;code&gt;x * y + z&lt;/code&gt; 有什么区别？”关键恰恰在那个 &lt;strong&gt;Fused&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;普通写法 &lt;code&gt;x * y + z&lt;/code&gt; 往往分两步走：先计算 &lt;code&gt;temp = x * y&lt;/code&gt;。在这个过程中，结果会被舍入到浮点数格式能够表示的精度内，例如 &lt;code&gt;double&lt;/code&gt; 的 53 位有效数字。这里已经丢失了一次精度。接着再计算 &lt;code&gt;result = temp + z&lt;/code&gt;，这里又会进行一次舍入；于是总共有两次舍入误差。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;std::fma(x, y, z)&lt;/code&gt; 根据 IEEE 754 标准，必须像拥有无限中间精度一样计算 &lt;code&gt;x * y + z&lt;/code&gt; 的精确值，然后只在最后把结果存回浮点格式时，进行唯一一次舍入。这意味着 &lt;code&gt;std::fma&lt;/code&gt; 通常比普通乘加更精确。尤其当 &lt;code&gt;x * y&lt;/code&gt; 的结果和 &lt;code&gt;z&lt;/code&gt; 大小相近而符号相反、发生灾难性抵消时，&lt;code&gt;std::fma&lt;/code&gt; 能保留更多有效位。&lt;/p&gt;
&lt;p&gt;现代 CPU，例如 Intel Haswell 及其之后的架构、ARM Cortex-A 系列等，通常都在 ISA 层面直接支持 FMA 指令，例如 x86 的 FMA3，或者历史上的 FMA4 指令集。&lt;code&gt;std::fma&lt;/code&gt; 常常会被编译成一条汇编指令，例如 &lt;code&gt;vfmadd213sd&lt;/code&gt;。这意味着它不仅精度更高，而且速度极快，通常只需 4-5 个时钟周期，并且吞吐量很高。&lt;/p&gt;
&lt;p&gt;但如果硬件不支持，编译器就可能调用软件库来模拟无限中间精度。这时候它会非常慢。标准定义了三个可选宏：&lt;code&gt;FP_FAST_FMA&lt;/code&gt;、&lt;code&gt;FP_FAST_FMAF&lt;/code&gt;、&lt;code&gt;FP_FAST_FMAL&lt;/code&gt;，用来向你通报地面实况：这台机器上的 FMA 到底是不是“快”的。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;想象一下，你写了一个 &lt;code&gt;std::list&amp;#x3C;int&gt;&lt;/code&gt;，传进去的分配器是 &lt;code&gt;std::allocator&amp;#x3C;int&gt;&lt;/code&gt;。可是链表内部并不直接存储孤零零的 &lt;code&gt;int&lt;/code&gt;，它存的是节点 &lt;code&gt;Node&amp;#x3C;int&gt;&lt;/code&gt;。分配器只会分配 &lt;code&gt;int&lt;/code&gt; 大小的内存，那么 &lt;code&gt;Node&lt;/code&gt; 里的指针域 &lt;code&gt;next&lt;/code&gt; / &lt;code&gt;prev&lt;/code&gt; 放到哪里去？这里就需要进行分配器变性，也就是 allocator rebinding；它早期正是通过一个 &lt;code&gt;rebind&lt;/code&gt; 结构体实现的。&lt;/p&gt;
&lt;p&gt;在 C++11 之前，例如 C++98，标准要求每个 allocator 都要手写一个 &lt;code&gt;rebind&lt;/code&gt; 结构体。到了 C++11 及以后，&lt;code&gt;std::allocator_traits&lt;/code&gt; 会通过 SFINAE 技巧检测用户是否手写了 &lt;code&gt;rebind&lt;/code&gt;：如果有，就使用用户自定义版本；如果没有，就自动替换模板参数，生成对应的分配器类型。&lt;/p&gt;
&lt;p&gt;什么是 SFINAE？它的全称是 Substitution Failure Is Not An Error：如果在替换模板参数的过程中产生了无效代码，例如访问一个不存在的类型，编译器不会立刻报错，而是认为这个重载不匹配，继续寻找下一个候选项。需要注意的是，SFINAE 只保护直接上下文中的替换失败，包括返回值类型、函数参数类型、模板参数默认值等；它并不保护函数体内部的代码。&lt;/p&gt;
&lt;p&gt;第一代、也最经典的 SFINAE 工具就是 &lt;code&gt;std::enable_if&lt;/code&gt;。它的原理是利用偏特化：如果条件为真，它就有一个 &lt;code&gt;type&lt;/code&gt; 成员；如果条件为假，它就没有 &lt;code&gt;type&lt;/code&gt; 成员，从而触发 SFINAE，让这个函数从候选集中消失。请看 Gemini 为我生成的伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;type_traits&gt;
#include &amp;#x3C;iostream&gt;

// 版本 1：只在 T 是浮点数时才存在
template &amp;#x3C;typename T&gt;
typename std::enable_if&amp;#x3C;std::is_floating_point&amp;#x3C;T&gt;::value, void&gt;::type
process(T t) {
    std::cout &amp;#x3C;&amp;#x3C; &quot;Processing floating point: &quot; &amp;#x3C;&amp;#x3C; t &amp;#x3C;&amp;#x3C; std::endl;
}

// 版本 2：只在 T 是整数时才存在
template &amp;#x3C;typename T&gt;
typename std::enable_if&amp;#x3C;std::is_integral&amp;#x3C;T&gt;::value, void&gt;::type
process(T t) {
    std::cout &amp;#x3C;&amp;#x3C; &quot;Processing integer: &quot; &amp;#x3C;&amp;#x3C; t &amp;#x3C;&amp;#x3C; std::endl;
}

int main() {
    process(3.14); // 匹配版本 1，版本 2 被 SFINAE 丢弃
    process(42);   // 匹配版本 2，版本 1 被 SFINAE 丢弃
    // process(&quot;Hello&quot;); // 两个都失败，此时才是真正的 Compile Error
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;里面那个 &lt;code&gt;typename&lt;/code&gt; 纯粹是 C++ 语法问题。因为在模板实例化之前，编译器默认会把作用域解析运算符后面的东西理解为变量或值。那么为什么有时要写，有时又不用写？核心在于 dependent names，也就是依赖名称：编译器是否能够在当前阶段消除歧义。&lt;/p&gt;
&lt;p&gt;例如 &lt;code&gt;std::vector&amp;#x3C;int&gt;::iterator&lt;/code&gt; 不是依赖名称。因为 &lt;code&gt;std::vector&amp;#x3C;int&gt;&lt;/code&gt; 已经完全确定，编译器查一下就知道 &lt;code&gt;iterator&lt;/code&gt; 是个类型，所以不需要写 &lt;code&gt;typename&lt;/code&gt;。而 &lt;code&gt;T::iterator&lt;/code&gt; 是依赖名称，因为 &lt;code&gt;iterator&lt;/code&gt; 的含义依赖于 &lt;code&gt;T&lt;/code&gt; 到底是什么。对于依赖名称，编译器默认它是值，必须用 &lt;code&gt;typename&lt;/code&gt; 明确标记它是类型。还有一个与此相似的语法陷阱，叫做 &lt;code&gt;.template&lt;/code&gt;，或者 &lt;code&gt;-&gt;template&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template &amp;#x3C;typename T&gt;
void call_foo(T&amp;#x26; t) {
    // 错误！编译器会以为这是：(t.foo &amp;#x3C; 3) &gt; (5)
    // t.foo&amp;#x3C;3&gt;(5);

    // 正确！告诉编译器 &amp;#x3C; 是模板参数列表的开始
    t.template foo&amp;#x3C;3&gt;(5);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到了 C++17，我们有了更优雅的技巧，专门用来探测“这个类有没有某个成员”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;std::void_t&amp;#x3C;...&gt;&lt;/code&gt; 的作用是：不管你往里面塞什么类型，只要它们都是有效类型，结果就是 &lt;code&gt;void&lt;/code&gt;；如果其中某个表达式无效，就触发 SFINAE。还是 Gemini 例子时间，这次检测一个类有没有 &lt;code&gt;reserve()&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;type_traits&gt;
#include &amp;#x3C;vector&gt;
#include &amp;#x3C;iostream&gt;

// 主模板：默认并没有 reserve
template &amp;#x3C;typename T, typename = void&gt;
struct has_reserve : std::false_type {};

// 特化版本：利用 SFINAE 探测
// 如果 T.reserve(size_t) 合法，std::void_t&amp;#x3C;&gt; 变成 void，匹配这个特化。
// 如果不合法，这行代码无效，SFINAE 踢掉这个特化，回退到主模板。
template &amp;#x3C;typename T&gt;
struct has_reserve&amp;#x3C;T, std::void_t&amp;#x3C;decltype(std::declval&amp;#x3C;T&gt;().reserve(1U))&gt;&gt;
    : std::true_type {};

int main() {
    std::cout &amp;#x3C;&amp;#x3C; has_reserve&amp;#x3C;std::vector&amp;#x3C;int&gt;&gt;::value &amp;#x3C;&amp;#x3C; std::endl; // 1 (True)
    std::cout &amp;#x3C;&amp;#x3C; has_reserve&amp;#x3C;int&gt;::value &amp;#x3C;&amp;#x3C; std::endl;              // 0 (False)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;std::declval&amp;#x3C;T&gt;()&lt;/code&gt; 这个东西非常有意思：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template &amp;#x3C;typename T&gt;
typename std::add_rvalue_reference&amp;#x3C;T&gt;::type declval() noexcept;
// 也就是返回 T&amp;#x26;&amp;#x26;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 C++11 引入 &lt;code&gt;decltype&lt;/code&gt; 之后，我们经常需要向编译器发问：“如果我有两个变量 &lt;code&gt;x&lt;/code&gt; 和 &lt;code&gt;y&lt;/code&gt;，让它们相加 &lt;code&gt;x + y&lt;/code&gt;，得到的结果类型是什么？”如果手上有实例，这很好办；但在模板元编程中，我们通常只有类型 &lt;code&gt;T&lt;/code&gt;，没有实例。此时写下 &lt;code&gt;decltype(std::declval&amp;#x3C;T&gt;().foo())&lt;/code&gt;，编译器会对这个表达式进行语义分析；又因为这里处于类型参数位置，也就是直接上下文，所以一旦推导失败，就会命中 SFINAE 的规则，使这个选项从候选集中被排除。不过，&lt;code&gt;std::declval&lt;/code&gt; 只能用于不求值语境中。&lt;/p&gt;
&lt;p&gt;幸运的是，后来多态内存资源（PMR, Polymorphic Memory Resources）和 Concepts 的引入，终结了许多晦涩难懂的模板天书。&lt;/p&gt;
&lt;p&gt;在 PMR 之前，Allocator 是容器类型的一部分。&lt;code&gt;std::vector&amp;#x3C;int, AllocA&gt;&lt;/code&gt; 和 &lt;code&gt;std::vector&amp;#x3C;int, AllocB&gt;&lt;/code&gt; 是两个完全不同的类型。这意味着你不能把它们传给同一个普通函数，除非把函数也写成模板；结果就是代码膨胀严重，接口极度不灵活。&lt;code&gt;std::pmr&lt;/code&gt; 利用虚函数和类型擦除机制，把具体的内存分配策略藏进运行时。现在，&lt;code&gt;std::pmr::vector&amp;#x3C;int&gt;&lt;/code&gt; 就是一种类型；无论底层资源是 &lt;code&gt;new_delete_resource&lt;/code&gt;，还是手搓的 &lt;code&gt;monotonic_buffer_resource&lt;/code&gt;，容器类型都保持不变。本质上，这是用虚函数调用的开销，换取代码体积缩小和接口简洁性；这是非常典型的工程权衡。&lt;/p&gt;
&lt;p&gt;过去用 &lt;code&gt;enable_if&lt;/code&gt; 配合 SFINAE，写出来的代码满屏尖括号。一旦报错，编译器会吐出几千行“模板实例化失败”堆栈，让人根本看不懂条件到底缺在哪里。Concepts，也就是编译期约束，让我们可以直接用近似自然语言的方式描述类型要求。&lt;/p&gt;
&lt;p&gt;黑暗时代：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template &amp;#x3C;typename T,
          typename = typename std::enable_if&amp;#x3C;std::is_integral&amp;#x3C;T&gt;::value&gt;::type&gt;
void foo(T t) { /* ... */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;光明时代：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void foo(std::integral auto t) { /* ... */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;总的来说，PMR 终结了 Allocator 导致的类型碎片化。它把复杂性从编译期类型系统转移到运行时对象状态，让代码更接近传统 OOP。Concepts 则终结了 SFINAE 的晦涩语法。它把隐晦的替换失败变成显式的约束检查，让模板编程从黑魔法降格为常规工程，也让 C++ 那种近乎危险的锋利感，终于有了一层可以被人类握住的刀柄。&lt;/p&gt;</content:encoded><category>现代 C++</category><category>浮点数</category><category>模板元编程</category></item><item><title>关于 AI 的一条暴论</title><link>https://atelier.moesegfault.dev/zh/blog/2025-11-20-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-11-20-1-zh/</guid><description>多数院校、企业和个人，并没有真正理解 AI；他们理解的只是围绕 AI 的焦虑、口号和跟风姿态。</description><pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我要发表一条暴论。&lt;/p&gt;
&lt;p&gt;多数院校、企业和个人，其实并没有真正理解 AI。正经地说，是组织惯性过大，尚未建立 AI-native 的标准、流程与评价模式；直白地说，就是在面对 AI 时集体跟风，看见所有人都说要 AI，FOMO 一上头，也开始宣布自己必须拥有这个东西。&lt;/p&gt;
&lt;p&gt;这种认知失调，只要看看某些团队一边声称要招“会用 AI 的人”，一边又在面试中禁用 AI，甚至把问题道德化、上升到诚信高度，就已经足够清楚。一般而言，招聘是为了找到能够解决问题的人；管理也应当首先是结果管理：合规、服从团队工作流、不制造生产事故、按时完成交付。至于具体如何解决问题，那叫 agency，本质上无非是用 Google 还是用 ChatGPT 的工具选择。&lt;/p&gt;
&lt;p&gt;结果操作起来，却放着真正稀缺的项目经验和工作经验不问，偏要拿 AI 能够秒杀的标准化问题反复折腾，仿佛要和工具本身拼效率；最后再可爱地补上一句“我们也要 AI 赋能”。这不就是荒唐吗？&lt;/p&gt;
&lt;p&gt;许多人对 AI 的理解也止步于“代码生成器”的层面，尚未意识到 AI 正在改变知识的组织方式，以及人类注意力的使用方式。至于所谓“不放心”，很多时候不过是面对未知协作模式时的本能恐惧。更有甚者开始用 AI 筛简历，这几乎是典型的错误用法；亚马逊早已替所有人做过实验。于是，很多垂直场景中的 AI 化，正经说叫探索，直白说就是搞 AI 噱头。&lt;/p&gt;</content:encoded><category>AI</category><category>AI-native</category><category>随想</category></item><item><title>拒绝做题：从标准答案到风险意识</title><link>https://atelier.moesegfault.dev/zh/blog/2025-10-20-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-10-20-1-zh/</guid><description>应试最深的副作用，不在于它要求人完成题目，而在于它把世界训练成一套可被裁判的标准答案：奖励“正确”，惩罚“错误”，并在长期的否定性反馈中，使人对犯错、偏离与风险本身产生恐惧。</description><pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我历来不喜欢做题，更准确地说，是不喜欢应试。这并不是出于那种“已经无需做题”的资源背景，我也不认为参与应试有什么丢人之处。正如我曾与群友分享过的一个判断：如果无法参与美股而选择国内理财，那叫理性决策；如果明明可以参与美股却依然选择国内理财，那叫认知低了。当然，这不包括那些技术上足以驾驭 A 股的高段位玩家。这个比喻更多是想说明，很多时候并不是人不行，而是没有足够好的池子可供选择；于是，不同约束条件下会生成不同的理性。&lt;/p&gt;
&lt;p&gt;应试最深的副作用，是向人灌注一种极坏的习惯：害怕犯错，害怕偏离某个既定标准。因为试题预设答案，也预设裁判；它奖励“正确”，贬斥“错误”。人在一个长期否定错误的环境里，很容易把犯错本身理解为人格或能力的失败，而不是探索过程中的必要成本。&lt;/p&gt;
&lt;p&gt;然而事实上，如果没有试错，人很难真正明白自己想要什么。世界上的知识领域如此广阔，无知对每个人而言都是常态。你或许可以成为某些方面的专家，却也必须承认自己在另外一些方面的不足。方向感远比“做了多少工作”更重要；不能指望用执行能力去遮蔽决策能力的贫乏。&lt;/p&gt;
&lt;p&gt;现实总是布满模糊地带，而风险与收益也永远是同一枚硬币的两面。高明的决策并不从执行的熟练度中自动涌现，它只会在现实的风浪、压力与权衡中逐渐酝酿。如果一个人对风险、对错误形成了习得性无助，自然也就隔绝了迭代的可能。即使谈到执行能力，许多人也并非出于自驱与成就感而行动，而是在进行某种苦行僧式的修行。依我看，忙碌终究掩盖不了意义的空洞。许多人努力“做题”，不是因为他们对知识怀有好奇，或从创造中得到快乐，而只是因为他们已经习惯了以避免惩罚为行动原则。&lt;/p&gt;</content:encoded><category>自我成长</category><category>反应试</category><category>风险意识</category></item><item><title>组合即世界观</title><link>https://atelier.moesegfault.dev/zh/blog/2025-10-09-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-10-09-1-zh/</guid><description>选择之所以难以彼此理解，往往不是因为答案不同，而是因为背后的地图不同；人的动人之处不在于承受了多少苦难，而在于面对苦难时仍能主动选择爱、善与正义。</description><pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;组合即世界观。&lt;/p&gt;
&lt;p&gt;我有时会觉得别人的选择难以理解，但那往往并不是单个判断出了偏差，而是整套世界观已经不在同一坐标系里。每个人其实都是握着整张地图的某一角在下注，因此无论对错，终究都有得有失；只是有的人看见的地形更多，拿到的筹码也更重。&lt;/p&gt;
&lt;p&gt;作为一个读理工科的人，我必须知晓并承认：世界并不是由技术单向决定的，更不会只围绕自身技能展开。我或许更理解某个局部，并且以此谋生，但这并不意味着其余知识就不能让我把人生过得更深、更稳、更自由。&lt;/p&gt;
&lt;p&gt;我不能只有专业技能，却对社会、政治与经济只保留一层轻薄的想象。技能只是人的一个维度；真正能够装水的木桶，是丰富且富有层次的知识结构，而持续学习的能力与心境，才是使其常青的水源。&lt;/p&gt;
&lt;p&gt;至于有人选择在我以为的沉船上起舞，我并不觉得有什么好指摘的；只是想清楚了是勇士，没想清楚便近乎烈士。我一直认为，一个人的动人之处从来不在于遭逢了多少苦难，而在于面对苦难时仍然展现出的坚韧与勇气。&lt;/p&gt;
&lt;p&gt;所以在文艺作品中，即便同样是各种意义上的牺牲与悲剧，有些人令人敬仰并感到震撼，因为他们是知其不可为而为之；有些人则只令人遗憾而哀婉，因为他们尚未看清来路，就已经不明不白地止步于此。&lt;/p&gt;
&lt;p&gt;类似地，道德的光芒不在于对规训的恪守，而在于自由之下主动选择爱、善与正义。因为从小接收的贞操观念而条件反射式应激，是小粉红；即使看见了最幽暗、最深邃的一面，却仍然选择行动，才是爱国者。&lt;/p&gt;
&lt;p&gt;同样，一个选择可以是面对现实的勇气，也可能只是认知水位太低，在相关知识上近乎一片空白。这里并没有那么现成的是非，只是每个人都压下自己的筹码，把心愿托付给时间；千古功过自有后人评说，做好自己的事情即可。&lt;/p&gt;</content:encoded><category>学习心得</category><category>世界观</category><category>伦理</category></item><item><title>运行时运算的边界究竟在哪里？</title><link>https://atelier.moesegfault.dev/zh/blog/2025-10-06-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-10-06-1-zh/</guid><description>当操作数并不源自 IO，所谓运行时往往只是工程取舍，而非本体论命运。</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一个运算究竟要怎样才可以被称为运行时的？&lt;/p&gt;
&lt;p&gt;我们向来可以把程序视作一个映射：它由一系列有序的 operators 构成。那么，若一个运算必须发生在运行时，便意味着该运算的操作数必须通过发起 IO 获得；除此之外，严格说来，它都可以在编译期完成，只是工程取舍的问题。&lt;/p&gt;
&lt;p&gt;自 C++98 起，模板系统已经具备图灵完备性，由此开启了编译期计算的先河。后来的 &lt;code&gt;constexpr&lt;/code&gt; 与 &lt;code&gt;consteval&lt;/code&gt;，更多是在这一脉络上的工程化优化；毕竟，消灭常数因子往往是非常诱人的。&lt;/p&gt;
&lt;p&gt;显然，operators 的操作数并不必然用于数学运算。若审视内存管理的实质，它其实是由特定数据结构维护分配信息；内存操作的实际意义，就是修改那本账簿。&lt;/p&gt;
&lt;p&gt;这就有意思了。我们都知道，一个数据结构若要便于合并与修改，通常会采用指针实现，以避免频繁拷贝的开销；何况在现代 C++ 中，游标实现本质上也仍是指针式实现。也就是说，把内存管理硬编码进算法逻辑，是一个伪需求；它理应通过模板元编程，统一交给分配器策略处理。若算法与数据结构课程至今还执着于三种线性表实现方法，我个人认为，这多少有些太前现代了。&lt;/p&gt;
&lt;p&gt;明确这个前提以后，再来审视指针实现的固有问题，其实就是内存不连续所造成的访存性能瓶颈。更根本的问题则是：我们为什么需要堆上分配？因为有些记账确实需要运行时信息。&lt;/p&gt;
&lt;p&gt;说到这里，大家应该知道我想说什么了。散列表驱动的状态机、RESTful 类 API 路由的前缀树，等等；如果注册的服务不需要热更新，就可以通过编译期运算，在保留指针语义的同时分配固定的连续内存，从而改善局部性，初始化时加载数据即可。即使需要热更新，只要采用预留空间，或更新本身并不频繁，这部分开销依然可以被压下去许多。&lt;/p&gt;
&lt;p&gt;而且，如果在这里使用策略驱动的模板元编程，将分配逻辑与增长逻辑解耦到分配器之中，你需要付出的不过是一点编译时间；省下的，却可能是整个团队极其可观的维护开销。&lt;/p&gt;</content:encoded><category>Modern C++</category><category>模板元编程</category><category>内存管理</category></item><item><title>Reddit 另类数据管线：把梗量化为市场信号</title><link>https://atelier.moesegfault.dev/zh/blog/2025-10-05-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-10-05-1-zh/</guid><description>不少量化团队确实在抓取 Reddit 数据，将其加工为 sentiment index；在这个意义上，梗图、哀嚎与狂热本身都可能成为市场的可观测量。</description><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Reddit、X、微博这些平台上的舆论动态，本质上是一个带有时滞的反馈系统：人们会对价格变化反应过度（overshoot），随后又进行过度纠正（overcorrect）。在控制论（cybernetics）的语境里，这几乎可以被理解为一种二阶振荡系统（second-order oscillation system）。&lt;/p&gt;
&lt;p&gt;因此，市场的顶部与底部，未必只是估值模型的边界条件；它们也可能是人类集体在进行非线性反馈下的自激振荡。听起来像某种混沌方程，但 Reddit 恰好就是那个混沌函数的观测窗口：Reddit 是全球化金融民主化最典型的实验区之一，人人都能说话，人人都能炒股，人人都能错。也正因为如此，它往往比机构研究报告更早显露出群体错觉凝结成型的瞬间。&lt;/p&gt;
&lt;p&gt;事实上，不少量化团队确实在抓取 Reddit 数据，并将其加工为 sentiment index：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Post Volume&lt;/strong&gt;：帖子数量暴涨 -&gt; 散户参与率激增。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comment Tone&lt;/strong&gt;：正面/负面情绪比例 -&gt; 短期多空情绪。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Meme Frequency&lt;/strong&gt;：“to the moon”“diamond hands”等表达的出现频次 -&gt; 泡沫化程度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loss Posts&lt;/strong&gt;：“my life is ruined” 型贴子暴增 -&gt; 局部底部信号。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个思路的起点之一，是 2019-2020 年酝酿、2021 年爆发的 GameStop 狂潮前后。一些数据科学家发现，WSB 帖子量与 GME 股价之间存在显著正相关。于是他们写出小脚本，用 Python + PRAW + VADER 情感分析，每日统计关键词出现频率、评论正负面比例。&lt;/p&gt;
&lt;p&gt;很快，这批人意识到，这套东西不只能够观察 GME，还可以作为散户资金动能的早期指标（retail sentiment proxy）。换句话说，Reddit 不再只是交易者情绪的垃圾场，而是散户流动性的前震仪。&lt;/p&gt;
&lt;p&gt;2021 年以后，哥伦比亚大学、MIT、芝加哥大学等机构也陆续出现相关研究：Reddit 热帖的情绪向量变化率，能够预测 1-3 日内的小盘股波动；在 meme stock 期间，帖子回复深度（comment depth）与波动率高度相关；当帖子中的“讽刺性乐观”（sarcastic optimism）上升时，往往意味着行情末期的 FOMO 峰值。&lt;/p&gt;
&lt;p&gt;所以，这就不再是单纯的玩梗，而是实打实的群体心理量化。&lt;/p&gt;
&lt;p&gt;现在主流大致有三派算法党：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;关键词统计派&lt;/strong&gt;：对帖子和评论进行词频统计与情感打分，例如 TF-IDF + VADER/FinBERT。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语义嵌入派&lt;/strong&gt;：直接用 embedding 生成语义向量并进行聚类，例如 Sentence-BERT / OpenAI Embeddings。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络动力学派&lt;/strong&gt;：将用户互动关系建图，用以分析舆情传播速度，例如 Graph Neural Networks / Diffusion Model。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些方法本质上都在追求同一件事：找到群体情绪的“二阶导数”，也就是情绪变化的变化率。因为那才是市场转向之前最危险、也最诱人的临界信号。&lt;/p&gt;
&lt;p&gt;别看 WSB 平日里闹腾得像金融市场的地下室，实际上，对冲基金早就开始抓取 Reddit、X、StockTwits、Bilibili 财经区等平台数据。他们把这套体系称为：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alt-Data Pipeline（另类数据管线）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Bloomberg 终端里，这类数据源已经逐渐成为主流。用一句不太体面但相当准确的话说就是：“机构看你发梗，机构做你对手盘。”&lt;/p&gt;
&lt;p&gt;量化 subreddit 的神妙之处在于，它完成了一个跨越：从“人类表达” -&gt; “群体情绪场” -&gt; “市场可观测量”。这几乎是把“认知科学”与“量化金融”强行缝合在一起。从某种意义上讲，它就是社会意识的傅里叶分析。&lt;/p&gt;
&lt;p&gt;它也说明了一个事实：现代金融早已不只是算账的艺术，而是心理学的实时仿真。&lt;/p&gt;</content:encoded><category>量化金融</category><category>另类数据</category><category>市场情绪</category></item><item><title>并发与并行：在计算边界上起舞</title><link>https://atelier.moesegfault.dev/zh/blog/2025-10-01-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-10-01-1-zh/</guid><description>从 OpenMP、pthreads 到 CUDA，并行化并非只是工具链的扩张，而是对计算能力、硬件边界与形式化模型的一次重新解释。</description><pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;并发与并行是两个时常被并置、也常常被混用的概念。自 2010 年代的多核时代以来，算法设计和架构考量早已越出单线程经典范畴；基础算法领域的经典著作《算法导论》也在更新中加入了对并行算法的讨论，不过仍有声音认为，其篇幅和深度远未充分。&lt;/p&gt;
&lt;p&gt;按照我个人更愿意采信的定义，并发指的是同时启动多个任务；并行指的是同时执行多个任务。由于 CPU 调度的基本单位是内核线程（或轻量级进程），单纯以进程作为衡量尺度，并不是一个足够精确的区分标准；这也使得多进程与多线程在环境独立性和运行开销之外，在应用层的体感上往往高度相似。&lt;/p&gt;
&lt;p&gt;并发一直是实战中极其棘手的区域。那些不知何时发生的中断与调度，很容易催生形态诡异的竞态条件；对象生命周期也随之变得暧昧，仿佛全靠程序员脆弱的心智模型和团队约定勉力维系。尤其当高权限的内核进程来回穿梭时，整个画面就会呈现出一种近乎残酷的美妙。&lt;/p&gt;
&lt;p&gt;并行化则是时代的大趋势。从 OpenMP、pthreads 到后来的 CUDA，其背后其实是对计算能力的一次重新诠释：传统 RAM 模型搭配渐进复杂度分析，固然有效指导了过去数十年的算法设计，但在多核时代终究显出不足，于是 PRAM、LogP 等模型应运而生。这些形式化方法对现实工程问题大有裨益，因为它们描述着能力的边界，以及使用这些能力的框架。&lt;/p&gt;
&lt;p&gt;当然，若继续追问这种计算能力的源泉，答案终究还是硬件进步。多核并行化不同于指令级并行、超标量或向量化；它更多由超线程、推拉迁移与亲和性、对称或异构多处理等机制共同绘就其波澜壮阔的底色。这些内容在优化层面尤其值得关注，因为它们并不只是背景知识，而是性能边界本身。&lt;/p&gt;
&lt;p&gt;即使到了 AI 打开一片新天地的今天，分布式、系统编程、高性能计算依旧是压在头顶的三座大山。若允许一点个人色彩和哲学意味：这些领域本就是计算机在“计算之近似”的边界上跳舞，形式化方法与现实系统之间的张力，在这里显露得淋漓尽致。&lt;/p&gt;</content:encoded><category>计算机科学</category><category>并发</category><category>并行计算</category></item><item><title>人际关系的冷启动：交心、交换与价值的可见性</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-29-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-29-1-zh/</guid><description>社交大体有两种形态：一种是交心，另一种是交换。交心看机缘，交换则是常态；真正的问题不在算计，而在价值如何被看见、被感受、被往复。</description><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我的一位朋友最近取得了一些颇为实在的成功；不过她真正内耗的地方在于：“人家凭什么无缘无故对你这么好？”她说她始终想不明白这件事。&lt;/p&gt;
&lt;p&gt;往宏观处说，一是国人普遍缺乏配得感，二是许多优秀的人或多或少容易陷入冒名顶替综合征。我的看法倒也简单：别人对你好不好，首先是别人的判断和心情，不必替别人完成心理审计；而且如果你确实有价值，别人对你好反而应当被视作常态。&lt;/p&gt;
&lt;p&gt;我个人认为，社交大体有两种形态：一种是交心，另一种是交换。交心几乎全看机缘，交换则是常态。价值交换的核心并不在于给别人带来多少实物层面的好处，而在于让别人确实感到有所收获：无论是智识上的，抑或是情绪上的，等等。说得功利一点，就是要给别人一个选择合作的动机。&lt;/p&gt;
&lt;p&gt;许多人害怕欠下人情债，因此多少有些放不开手脚：给了别人以后，也不敢从别人那里拿。这是一个很常见的失误。良好的人际关系应该是动态平衡的，单纯倒向任何一边，都是对称意义上的不健康；单方面付出以换取“好感”，并不是交换。即使在中国传统的乡土社会里，人情关系也是彼此相欠而不能简单“算账”的。总的来说，既然我选择给予，就要有与之相称的、拿得住的气度。&lt;/p&gt;
&lt;p&gt;大多数情况下，可以选择先给予，因为这会释放出合作的信号。关系总是双向奔赴的：既要我给别人更进一步的机会，也要别人愿意和我更进一步。蝇头小利根本无关紧要；如果能够因此收获一个深度合作的伙伴，回报就不可估量。&lt;/p&gt;
&lt;p&gt;“德备而后物至”。当然，酒香也怕巷子深，但那更多只是展开对话契机的问题；更关键的追问仍然是：我能够提供什么样的价值？奇货可居，真正稀缺的东西自会形成引力，那点算计的小心思其实根本不值一提。也不要陷入依仗自身能力的误区，以为能力强了就万事皆可。不能让别人有所收获的东西，即使自己拥有，因为不可交换，也并不构成价值。&lt;/p&gt;
&lt;p&gt;好为人师是绝对的大毛病。我可以分享自己的感受和观点，但绝不可以用它们覆盖别人的经验结构；这是对他人极大的不尊重。所以，别人不问，就不要说。别人如何，本来无关紧要；如果是关心的人，则尽力就好。&lt;/p&gt;
&lt;p&gt;然后，接下来的问题就是纯粹的技术问题，按公式做题即可，并没有什么难度；本质上只看有没有遇到对的人而已。我其实是情商很差的人，并没有细腻的情感体验，也缺乏那种天然的关怀能力，所以在这一块，我能明白的真的不多。&lt;/p&gt;
&lt;p&gt;所以，社交其实非常无聊；并不是因为那些无聊的小算计，而是因为它足够算法化，归根到底都是手法和操作。真正值得的是那些重要的人：花时间去陪伴，花时间去感受，去理解那些超越具身处境的故事，去消化那些自然而然流露出来的分享欲。我个人认为，单纯事业和智识的完满并没有那么值得渴求；或许更加丰富的体验，才是有限生命中真正值得追逐的东西。而金钱、知识，以及人的故事与感受，都只是其中的一部分。&lt;/p&gt;</content:encoded><category>自我成长</category><category>人际关系</category><category>随想</category></item><item><title>一篇值得细读的论文</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-13-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-13-1-zh/</guid><description>这指向科研范式的转换。以 PUCT (Predictor + UCB applied to Trees) 为例，面对这类任务，重心已从解题转向找题，转向设计能够精确反映科学目标的评价尺度。</description><pubDate>Sat, 13 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;读到一篇颇有启发的论文，&lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2509.06503v1&quot;&gt;An AI system to help scientists write expert-level empirical software&lt;/a&gt;&lt;/em&gt;。简记几点感想：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Gemini 的概括相当到位：一项研究的影响力，很大程度上取决于其如何定义并框架化自身试图解决的问题。将一个具体问题提升为普遍性挑战，是使研究成果获得外溢效应的关键步骤。作者并未将工作表述为“为若干基准测试任务找到更好的代码生成方法”，而是宣称其意在“加速科学发现的循环”。这种问题意识和叙事尺度上的差异，正是优秀论文与顶尖论文之间的分水岭。研究不应止步于“解决一个问题”；更重要的是学会如何框架化问题、泛化解决方案，并设计一套令人信服的评估策略来支撑论点。论文的叙事结构与证据链，和技术创新本身同样重要。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Gemini 的另一点判断也很准确：深刻的贡献并不必然要求从零开始发明一种全新的理论或算法。它也完全可以来自一种新的组合方式：将既有而强大的工具重新编排，进而解决任何单一工具都难以触及的问题。因此，研究者需要始终保有跨领域的视野，持续追问：“如果将 A 领域的某项强技术用于 B 领域的经典问题，会发生什么？”发现新连接、生成新组合的能力，本身就是创新的重要源泉。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这更像是科研范式层面的变革。就 PUCT (Predictor + UCB applied to Trees) 而言，面对这类任务，核心已从解题转向找题，转向设计足够精确、足以反映科学目标的度量标准。过去也许可以通过预先布局数据集，或掌握标准（即优化目标）的定义权，再由自己刷出 SOTA，来完成类似意义上的创新；但在新的路径下，这种路径的生命周期被显著压缩。定义优化方向、开辟新优化路径的能力，在任何时期都至关重要。如今创造性破坏已经抵达门前，那种被成果绩效绑架、甘愿充当执行者的研究方式，也就是在既有范式下重复 1 -&gt; 100 的增量创新，已经不再成立；因为单纯比拼执行力，不可能拼得过不知疲倦的 AI 系统。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;事实上，真正玩得起这类 AI 系统的只会是大型机构。对于个人而言，独立完成这种超复杂系统在工程量和资源投入上都极不现实。谁能率先在算力、数据与落地链路上完成布局，谁就可能主导下一个时代的科研生产；这几乎就是范式制定权的王座。归根到底，研究者必须适应新旧范式交替与创造性破坏。只会堆叠论文发表数量、服从导师权威的科研方式，正在迅速失去价值。因而对于个人而言，相较于更加传统的学术技能，真正 AI-native 的能力要实际得多。尽管许多人仍然假装看不见房间里的大象，继续沿用旧有培养方式，并且只把 AI 理解为一个新的创新与发表方向，但周期和规律并不会轻易服从人的主观意志。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我们也应当对可量化指标祛魅。过去，对可量化指标的过度依赖或许还可以被解释为一种效率上的 trade-off；但在工具层面，我们现在已经拥有更好的选择。无论如何，评价标准的改变都是必须适应的事实：招募再多执行者，也无法与一个能够提出问题、并在模糊地带持续探索的人竞争。反复建构各类标准化测试与选拔机制，表面上看似合理；但落到某些老旧品味手中，往往只是让古德哈特定律一再重演。因此，也不必抱怨招来的人和学生只会做题、做不了事情、做不出转化；更应当先反思一下，自己的判断品味究竟已经贫乏到什么程度。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>文献阅读</category><category>AI系统</category><category>随想</category></item><item><title>深入理解编译期斐波那契数列计算</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-08-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-08-1-zh/</guid><description>编译器优化的历史近似一场开源节流：一端是优化算法自身的演进，另一端则是持续向编译器供给更多信息，以推移语义约束的边界。若从现代 C++ 的视角重新审视模板元编程，就应回到模板作为过程族抽象与 policy-driven 设计工具的本义；用模板计算编译期斐波那契数列，更像是 C++11 之前不得不借道而行的技术折返。</description><pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;C++ 中一个常见的“面试题”是借助 &lt;code&gt;template&lt;/code&gt; 及模板元编程相关机制计算斐波那契数列。其实现并不复杂：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;

template &amp;#x3C;unsigned int n&gt;
struct Calculator
{
    static const unsigned int value = Calculator&amp;#x3C;n - 1&gt;::value + Calculator&amp;#x3C;n - 2&gt;::value;
};

template &amp;#x3C;&gt;
struct Calculator&amp;#x3C;1&gt;
{
    static const unsigned int value = 1;
};

template &amp;#x3C;&gt;
struct Calculator&amp;#x3C;2&gt;
{
    static const unsigned int value = 1;
};

template &amp;#x3C;unsigned int n&gt;
const unsigned int fibonacci = Calculator&amp;#x3C;n&gt;::value;

int main(void)
{
    std::cout &amp;#x3C;&amp;#x3C; fibonacci&amp;#x3C;5&gt; &amp;#x3C;&amp;#x3C; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其核心在于 C++ 特有的模板参数推导与匹配机制；这一机制实质上引入了分支选择：编译器会优先匹配“更适合”的参数。因此，我们将模板参数 &lt;code&gt;n&lt;/code&gt; 取 &lt;code&gt;1&lt;/code&gt; 与 &lt;code&gt;2&lt;/code&gt; 的情形特化为递归终点。&lt;/p&gt;
&lt;p&gt;同一思路在模板偏特化中也十分典型：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;

template &amp;#x3C;typename T&gt;
struct is_pointer
{
    static const bool value = false;
};

template &amp;#x3C;typename T&gt;
struct is_pointer&amp;#x3C;T *&gt;  // 偏特化！
{
    static const bool value = true;
};

template &amp;#x3C;&gt;
struct is_pointer&amp;#x3C;decltype(nullptr)&gt;
{
    static const bool value = true;
};

template &amp;#x3C;typename T&gt;
const bool is_pointer_v = is_pointer&amp;#x3C;T&gt;::value;

int main(void)
{
    std::cout &amp;#x3C;&amp;#x3C; is_pointer_v&amp;#x3C;decltype(nullptr)&gt; &amp;#x3C;&amp;#x3C; std::endl;
    std::cout &amp;#x3C;&amp;#x3C; is_pointer_v&amp;#x3C;decltype(2)&gt; &amp;#x3C;&amp;#x3C; std::endl;
    std::cout &amp;#x3C;&amp;#x3C; is_pointer_v&amp;#x3C;decltype(&amp;#x26;std::cout)&gt; &amp;#x3C;&amp;#x3C; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当表达式为 &lt;code&gt;&amp;#x26;std::cout&lt;/code&gt; 时，C++ 会匹配更合适的模板形式 &lt;code&gt;T *&lt;/code&gt;，于是 &lt;code&gt;value&lt;/code&gt; 被设定为 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当然，“更合适”只是便于叙述的说法；在标准层面，C++ 对推导与匹配过程有严格而细密的规定。&lt;/p&gt;
&lt;p&gt;需要注意的是，软件工程中早已有一条近乎冷酷的定律：复杂度不会消失，只会转移。模板实例化中的这类计算器，本质上是把原本属于运行期计算的复杂度平移到编译期。在绝大多数场景下，这一交换是值得的，因为我们审视实际上线的代码时通常采用渐进复杂度的框架：优化更关心增长率，尽管用户体验又提出了一个并不温和的补充命题——在相当多场景中，常数时间的改善同样不应被轻视。&lt;/p&gt;
&lt;p&gt;然而，作为现代 C++ 滥觞的 C++11，为这类编译期计算提供了更直接的选择：&lt;code&gt;constexpr&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们实际上只是在以绕行方式实现下列函数所表达的计算：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int fibonacci(int n)
{
    if (n == 1 or n == 2)
        return 1;
    else
        return fibonacci(n - 1) + fibonacci(n - 2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;过去真正的瓶颈在于，编译器并不知道这段计算应当被挪至编译期完成，于是我们只能借助 &lt;code&gt;template&lt;/code&gt; 迫使编译器进行计算。若审视编译器本身，其真正强大之处在于它维护并运用类型信息；编译过程在相当意义上是在完成一组构造性证明，而优化的边界，正是编译器所拥有信息的边界。因此，更自然的解决方式是向编译器显式提供信息，使其能够完成编译期求值；这正是 &lt;code&gt;constexpr&lt;/code&gt; 的位置。&lt;/p&gt;
&lt;p&gt;它向编译器&lt;strong&gt;建议&lt;/strong&gt;在编译期完成计算：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;

// 使用 constexpr 函数，可以进行编译期求值。
constexpr int fibonacci(int n)
{
    // C++14 之后，constexpr 函数体内可以容纳更丰富的控制逻辑，
    // 形式上已相当接近普通函数。
    if (n == 1 or n == 2)
        return 1;
    else
        return fibonacci(n - 1) + fibonacci(n - 2);
}

int main(int argc, char *argv[])
{
    // fibonacci(10) 是常量表达式，编译器可以在编译期直接求值。
    std::cout &amp;#x3C;&amp;#x3C; fibonacci(10) &amp;#x3C;&amp;#x3C; std::endl; // 编译后这里直接就是 std::cout &amp;#x3C;&amp;#x3C; 55 &amp;#x3C;&amp;#x3C; std::endl;

    // 也可以用它来定义数组大小。
    int arr[fibonacci(6)] = {}; // 定义一个大小为 8 的数组
    std::cout &amp;#x3C;&amp;#x3C; &quot;Array size: &quot; &amp;#x3C;&amp;#x3C; sizeof(arr) / sizeof(int) &amp;#x3C;&amp;#x3C; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过问题也正在这个“建议”之中；正如 C++ 模板存在递归深度限制一样，面对过深的编译期运算，或面对编译器无法确认其为常量表达式的语境，&lt;code&gt;constexpr&lt;/code&gt; 仍可能退回运行期求值。若要消除这种“建议性”，可以使用 &lt;code&gt;consteval&lt;/code&gt; 关键字：二者都属于向编译器提供信息、从而扩大优化空间的语言设施。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里有必要单独澄清斐波那契计算本身的复杂度。在前面的模板元编程示例中，每一个 &lt;code&gt;Calculator&amp;#x3C;n&gt;&lt;/code&gt; 都是一个独立类型；维护类型信息的编译器会保存这些实例，因此不会反复计算相同的子结构。其算法复杂度并非 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(2^n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em;&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6644em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，而是 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em;&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;：我们实际上只需要从 1 到 n 的每一种情形各计算一次，而通常 RAM 模型中的随机访问可视为常数时间。至于后面的 &lt;code&gt;constexpr&lt;/code&gt; 方法，从现代主流编译器实现的角度看，在较高优化等级下处理常量求值上下文时，编译器通常也会通过记忆化避免重复劳动。因此，在实践中，&lt;code&gt;fibonacci(10)&lt;/code&gt; 这类递归常量求值的编译耗时也更接近线性复杂度 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em;&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，而非指数级。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然，C++17 之后，&lt;code&gt;constexpr&lt;/code&gt; 还获得了某种近似“编译期分支重载”的表达能力，即 &lt;code&gt;if constexpr&lt;/code&gt;：它在编译期完成判断，并直接丢弃无效分支的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;
#include &amp;#x3C;type_traits&gt;

template&amp;#x3C;typename T&gt;
void print_info(T value) 
{
    if constexpr (std::is_pointer_v&amp;#x3C;T&gt;) 
    {
        // 如果 T 不是指针，下面这整块代码（包括花括号）
        // 都会被编译器丢弃，等同于从未进入实例化路径。
        // 因此即使 T 是 int，*value 这样的语法也不会导致编译失败。
        std::cout &amp;#x3C;&amp;#x3C; &quot;It&apos;s a pointer to: &quot; &amp;#x3C;&amp;#x3C; *value &amp;#x3C;&amp;#x3C; std::endl;
    } 
    else 
    {
        // 反之，如果 T 是指针，这块代码会被丢弃。
        std::cout &amp;#x3C;&amp;#x3C; &quot;It&apos;s a value: &quot; &amp;#x3C;&amp;#x3C; value &amp;#x3C;&amp;#x3C; std::endl;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;总的来说，这体现了 C++ 将更多语义前移至编译期处理的总体倾向。&lt;/p&gt;
&lt;p&gt;当然，如果坚持使用可读性极差的模板元编程来构造计算器，或者只是有意试探一些边缘技法，&lt;code&gt;constexpr&lt;/code&gt; 仍然有其用武之地：它可以制造编译期对象，并在功能表达与可读性之间提供一次相当显著的改善。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;

struct Point 
{
    double x, y;

    // 第一步：提供 constexpr 构造函数。
    // C++11 要求构造函数体为空，只能使用初始化列表。
    // C++14 之后限制放宽，函数体内可以包含更复杂的逻辑。
    constexpr Point(double x_val, double y_val) : x(x_val), y(y_val) {}

    // 第二步：希望在编译期调用的成员函数，也必须声明为 constexpr。
    constexpr double length_sq() const 
    {
        return x * x + y * y;
    }
    
    // 第三步（隐式）：析构函数必须是平凡的 (trivial)。
    // 默认析构函数即为平凡析构函数，因此无需显式写出。
    // ~Point() = default;
};

int main() 
{
    // 在编译期创建 Point 对象。
    constexpr Point p1(3.0, 4.0);
    constexpr Point p2(5.0, 12.0);

    // 在编译期调用成员函数，计算另一个常量。
    constexpr double len_sq = p1.length_sq(); // 编译器直接算出 25.0

    // 使用这一编译期计算结果定义数组。
    int my_array[static_cast&amp;#x3C;int&gt;(len_sq)]; // 创建一个大小为 25 的数组
    std::cout &amp;#x3C;&amp;#x3C; &quot;Array size: &quot; &amp;#x3C;&amp;#x3C; std::size(my_array) &amp;#x3C;&amp;#x3C; std::endl;

    // 还可以在编译期对这些对象进行运算。
    constexpr Point p3(p1.x + p2.x, p1.y + p2.y); // p3 是 (8.0, 16.0)
    
    std::cout &amp;#x3C;&amp;#x3C; &quot;p3.x = &quot; &amp;#x3C;&amp;#x3C; p3.x &amp;#x3C;&amp;#x3C; std::endl; // 这行代码在编译后，可能直接变成了 std::cout &amp;#x3C;&amp;#x3C; &quot;p3.x = &quot; &amp;#x3C;&amp;#x3C; 8.0 &amp;#x3C;&amp;#x3C; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;总的来说，我想指出的是：编译器优化的历史近似一场开源节流，一方面依赖优化算法自身的进步，另一方面则依赖我们向编译器提供更多信息，以推移语义约束的边界。如果从现代 C++ 的视角审视模板元编程，仍应回到 &lt;code&gt;template&lt;/code&gt; 抽象一类过程、服务于 policy-driven 设计的本义；用模板实现编译期斐波那契数列计算，更多是 C++11 之前不得不采取的技术折返，而模板递归实例化也正是所谓“模板天书”的重要源泉。&lt;/p&gt;</content:encoded><category>现代 C++</category><category>模板</category><category>编译期计算</category></item><item><title>高校造神批判札记</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-07-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-07-1-zh/</guid><description>正如学术品味需要长期磨练一样，对需求的识别同样包含大量隐性知识，也同样需要被训练；而这恰恰是支撑大学运行的那套叙事所拒绝提供的东西：它教你如何“解题”，却不教你如何“找题”。而“找题”的能力——也就是发现并定义有价值问题的能力——在未来会远比“解题”的能力更加珍贵。</description><pubDate>Sun, 07 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一个相当核心的问题在于，当下高校内部的“造神”机制已经发展到颇为严重的程度：诸如“全寝保研”、纸笔抄代码，乃至某某本科生又在顶会顶刊发表论文，皆可迅速被包装为可供膜拜的叙事。二十岁上下本就是最容易生产神话与偶像的年龄段，其后又紧接着人生中最具可塑性的十年求索；于是整体氛围便被塑造成一种近乎胁迫性的共识：如果不保研、不读博、不进入大厂，人生仿佛就已经宣告失败。&lt;/p&gt;
&lt;p&gt;我们通常也并不鼓励康德意义上的“敢于求知”；在某些语境里，求知本身甚至反倒成了一种僭越。凡事皆倾向于诉诸年资、经验与 title。学校也很少鼓励苏格拉底式的、通过对话与提问“引导人发现自己内心中的知识”的教育形态；相反，导师被设置为教育的主体，学生则被安置为接受与灌输的对象。更何况 STEM 语境中的许多人，虽已习惯可量化、可证伪的对象，却往往对“意义”这一问题缺乏同等程度的反思。&lt;/p&gt;
&lt;p&gt;更关键的问题是，我们其实都知道人才招聘中的供需关系。如今本研倒挂、硕士学制延长，在全面过剩的背景下，岗位究竟招收多少人，才是更具决定性的因素；换言之，在当前的大环境中，需求决定了你劳动的价格。由此产生的一种典型供给侧推断便是：应当提升自身学历的稀缺性；或者，想得再深入一些，提升自身能力的稀缺性。&lt;/p&gt;
&lt;p&gt;也正是在这里，第一性原理式思考的价值才显现出来。事实上，我们完全可以继续追问：需求只存在于大厂岗位之中吗？只存在于研究所与教职之中吗？归根到底，这些都只是显性的需求。那么，那些尚未被充分命名的不显性需求呢？我们习惯于接受“获取学历、打磨技能、然后进入各类组织上班”的叙事，却很少真正追问：在一个明明由需求驱动而非技术驱动的时代，为什么知识、技能，乃至学历这种符号化资本的稀缺性，会被置于对需求的品味之上？正如学术品味需要长期磨练一样，对需求的识别同样包含大量隐性知识，也同样需要被训练；而这恰恰是支撑大学运行的那套叙事所拒绝提供的东西：它教你如何“解题”，却不教你如何“找题”。而“找题”的能力——也就是发现并定义有价值问题的能力——在未来会远比“解题”的能力更加珍贵。这也同样解释了“学历不代表能力”的根源：如今你的价格不再主要由你的稀缺性主导，而是由用人方的需求主导。&lt;/p&gt;
&lt;p&gt;有位参加 Adventure X 的朋友曾在自己的博客中感慨：大学死气沉沉。这判断并不夸张；因为在一个重灌输而轻讨论、重复现而轻创造、重发文而轻落地的环境中，本就很难指望真正有生命力的东西生长出来。甚至许多顶会顶刊论文的实际价值，也处于一种相当暧昧的意义不明之中。更何况当下正处在 AI 持续崛起、并不断挤压人类熟练度价值的时刻：算法题做不过 AI，基础代码又已近乎泛滥，此时再固守这套铜墙铁壁般的评价秩序，就愈发显得不合时宜。&lt;/p&gt;</content:encoded><category>日常生活吐槽</category><category>AI-native</category><category>新可能</category></item><item><title>面向 CS 本科生的建议（免费、初阶版）</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-06-2-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-06-2-zh/</guid><description>年轻计算机人（包括我自己）应当尽早建立的一组低成本基础实践。</description><pubDate>Sat, 06 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;年轻计算机人（包括我自己）在早期阶段应当优先完成的事情（Free &amp;#x26; Beginner ver.）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在信息检索层面屏蔽 CSDN；&lt;/li&gt;
&lt;li&gt;形成遇到问题先询问 DeepSeek、Kimi 与 Qwen 的反射；&lt;/li&gt;
&lt;li&gt;非必要不浏览知乎，尤其不宜抱着“拓宽视野”的心态进入；&lt;/li&gt;
&lt;li&gt;新闻软件优先看网易新闻，而不是微博；&lt;/li&gt;
&lt;li&gt;掌握支付宝定投的基本用法；&lt;/li&gt;
&lt;li&gt;置办并维护足够数量的借记卡与信用卡（条件允许时）；&lt;/li&gt;
&lt;li&gt;专业知识来源锁定计算机科学丛书与 B 站搬运资料，不必迷信歪门邪道；&lt;/li&gt;
&lt;li&gt;尽早办妥护照和港澳通行证，并尽可能多储备签证；&lt;/li&gt;
&lt;li&gt;熟练使用 Outlook 和 To Do，或功能等效的邮件、日历、任务管理系统；&lt;/li&gt;
&lt;li&gt;理解并会用 Microsoft 365 全家桶所包含的能力（并不意味着必须购买）；&lt;/li&gt;
&lt;li&gt;积极使用 GitHub 薅代码，以及附带的 Copilot Free 和 *.github.io；&lt;/li&gt;
&lt;li&gt;娱乐就交给 B 站、小红书、抖音，切忌让娱乐反过来制造内耗；&lt;/li&gt;
&lt;li&gt;安装 Linux 虚拟机（不建议直接装到宿主机上，毕竟还要玩游戏），适应黑洞洞的终端，而不是把一切交给 IDE；&lt;/li&gt;
&lt;li&gt;国内的高质量信息会出没在豆瓣、微信公众号、知乎，但整体信噪比很低，必须理性入场；&lt;/li&gt;
&lt;li&gt;适度远离闲鱼，并在正确场景下理解和使用闲鱼、淘宝上的非常规路径，由此形成合规思维。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;目前只想到这些。它们无论在认知成本还是经济成本上都不高，真正消耗的是操作密度；不过只要足够灵活，总有路径可走。&lt;/p&gt;</content:encoded><category>日常生活吐槽</category><category>事业建议</category></item><item><title>评学校“AI实践课”</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-06-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-06-1-zh/</guid><description>​当下对深度学习的一拥而上，更近于 investor 的投机姿态，尚远未抵达 AI-native believer 的自觉。</description><pubDate>Sat, 06 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;开场即以权威姿态立规矩，气息近乎酱缸，我自然无意参与；一群连 ChatGPT Plus 都未曾订阅的人聚在一起谈“人工智能实践”，除却经典深度学习的回顾与 Transformer 之后的文献导读，我实在难以想象还能展开什么内容：若只是为了获取知识，我当然会求助于 AI；提示工程若运用得当，几乎等同于两位 PhD candidate 轮番为我提供智识劳动。
​
​对于并不志在 NLP / CV / RL 科研的人而言，关键并非投身各类奇诡的学术任务，并在应用层迁移中充当执行者，从而被塑造成学术人力资本；真正重要的是如何将 AI 作为工具嵌入自身工作流之中，此即所谓 AI-native。
​
​多数“实践”的症结正在于，它们只是将 AI 视作对既有范式的局部修补，而非一种全新交互范式的开启。因此才会出现这样的悖论：企业一方面声称要招募善用 AI 工具的人，另一方面又在结构化面试中禁用 AI。
​
​当下对深度学习的一拥而上，更多只是 investor 式的逐利姿态，远未抵达 AI-native believer 的自觉；并且如我过去所指出的，此类蜂拥往往并不理性。只要稍微了解 C 端现状与社区实践，便不至于维持这种近乎狂热的想象。&lt;/p&gt;</content:encoded><category>日常生活吐槽</category><category>AI-native</category></item><item><title>对B站推荐机制的一点异议</title><link>https://atelier.moesegfault.dev/zh/blog/2025-09-03-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-09-03-1-zh/</guid><description>我承认，自己在B站的使用频率不足，账号画像或许尚未得到充分训练；但其推荐算法的个性化仍然显得相当粗糙：它也许能够捕捉我的身份轮廓与兴趣方向，却无法匹配相应的内容深度和信息密度，而只是停留在标签层面的相似性上——信噪比过低，体验自然难称理想。</description><pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我的B站推荐流充斥着大量我并不愿意观看的内容，这使我此刻相当不悦：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;那些以面试题为中心的内容也应当适可而止。讨论应用层自旋锁却完全不考虑中断，还能称之为理解并发编程吗？至于写出一个既无法判断是否成环、也无法判断是否相交的链表，只能说明工程基础过于薄弱；若真在团队中遇到这样的合作者，要么先为其补足数据结构，要么干脆请其离场。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;那些讲算法题的内容同样难以引起我的兴趣，尤其是宣扬“不考”或“考得少”所以便不必学习的论调，几乎是在我的敏感区上反复试探。我希望接受的是以《离散数学及其应用》和《算法导论》为基础、再补入并行算法训练的系统教育，而不是单纯积累题型与解法模板。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;至于讲 modern C++ 和系统细节的视频，更容易激发我近乎苛刻的判断标准。我对某个语法表面上如何书写并无多少兴趣；我想看到的是对其演进趋势与内在价值的分析，而不是观赏某种花哨特性，或继续囤积零散而缺乏结构的知识点。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;好不容易看到一个似乎有趣的视频，点进去却发现只是搬运内容，且又套上了AI机翻语音，我便当场退出：在B站消费YouTube的机翻副本，究竟具有什么意义？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;科研方法论与学术主题视频，尤其是AI相关内容，为什么会如此泛滥？我并没有导师需要应付；平台不能因为我偶尔点开几个相关视频，就径直把我归类为研究生，然后持续推送一堆意义暧昧的深度学习内容和论文解读。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;那些讲数学的视频中确有一些有趣之作，但系统主要推送给我的却是考研数学和高考数学，以至于我一度怀疑它是在某种意义上“侮辱”我。我对堆叠复杂积分毫无兴趣：即便将其手算技巧推进到极致，也依然无法超过 symbolic computation；而一旦要求其讲数学史、讲证明、讲抽象，它又往往无话可说。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;还有一批以“985/211”“高中生××”之类表述作为标题卖点的内容，我同样缺乏兴趣。一个人的思想与知识价值并不来自其title，话语的可行性也应交由现实与逻辑共同检验。至于那些以年龄凸显天赋的叙事，未免显得乏味；真正的年龄与认知错位究竟有多令人不适，他们大概并未切身经验。与其把时间消耗在闭门造车上，不如承认自身局限，并尽可能站在前人的肩膀上重新观看问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;好不容易刷到一个愿意观看的游戏视频或美女博主，却又被一堆网络安全内容围住。我对密码学和网络安全的兴趣确实有限，专业事务也应当交由专业人士处理；我的品味，或者更准确地说，我的下意识判断，是从性能与系统角度评估事物，而不是首先从安全角度出发。我或许能够做一些高性能计算和系统编程，但并不适合成为安全专家。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我承认，自己确实在B站刷得太少，账号画像尚未被充分训练；但其推荐算法的个性化依然显得过于粗糙。它或许能够匹配“我是什么样的人”以及“我大致处于什么方向”，却无法进一步匹配足够的内容深度和信息密度，只是机械地停留在标签相似性上——信噪比太低，体验自然很差。&lt;/p&gt;</content:encoded><category>日常生活吐槽</category><category>哔哩哔哩</category></item><item><title>算法学习的理想与现实差距</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-28-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-28-1-zh/</guid><description>纯粹的形式模型往往是现实的一个投影，是一种选择性的反映，作为逻辑模型的算法在现实中也面临着适用范围的审慎。</description><pubDate>Thu, 28 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;算法，以及数据结构，在计算世界中几乎无处不在：从一次普通的函数调用，到计算机网络中的路由与负载均衡。在足够理论化的视角下，算法首先是一个逻辑模型，而具体代码只是它在某种程序设计语言中的翻译结果；也有更激进的讨论会以汇编作为统一基准，以尽可能排除实现层面的噪声，不过更通常的表达方式仍是伪代码——顺序、循环、条件已经足以承载相当强的表达力。&lt;/p&gt;
&lt;p&gt;我们对算法的一般印象，往往集中在渐进复杂度分析上，尤其是 RAM 模型下的渐进复杂度分析；这个简单而强大的模型有效指导了数十年的算法设计，并在 2010s 之后的多核时代仍然表现出相当的稳健性，至今也是本科教学的核心任务之一。不过，纯粹的形式模型往往只是现实的投影，是一种选择性的反映；作为逻辑模型的算法，在现实中同样需要被审慎地限定适用范围。以渐进复杂度分析为例，它反映的是理想模型下算法耗时的增长趋势，但在某些场景中，它并不总能与实际表现严格吻合。&lt;/p&gt;
&lt;p&gt;最直接的差异来自设备 I/O。一次磁盘 I/O 就足以迫使算法设计走向完全不同的方向：如果我们采用的模型只是一个计算单元、一个读写针头和一条无限纸带，那么 B 树相较于二叉树（即便后者通常带有平衡条件）未必会表现出压倒性的性能优势；但在数据库系统中，B 树能够显著减少查询次数，根节点甚至可以常驻主存，并且天然支持区间访问，因此获得了极其巨大的工程优势。&lt;/p&gt;
&lt;p&gt;另一个值得考量的问题是缓存。L1 缓存命中的诱惑几乎无法忽视：例如，相比于分离链表法散列表（也就是竞赛中常见的拉链法），开放定址散列表由于避免了若干次指针访问，在某些场景中完全可能更快。尤其是线性探测，虽然聚集问题相当明显，却又会带来 CPU 预取的优势。&lt;/p&gt;
&lt;p&gt;其次是复杂的并发环境。某些在串行模式下表现良好的算法与数据结构，一旦进入并发场景，简单加锁便可能引入锁竞争，并显著拖慢性能；内存顺序的合理配置也会构成不小的挑战。更极端的情形，则是试图实现无锁数据结构的种种技巧。并发编程本就处在相当暧昧的地带，实际可靠性往往依赖一系列原语提供担保，因此对 lock-free 的渴望固然理想主义十足，落到操作层面却常常骨感得近乎残酷。&lt;/p&gt;
&lt;p&gt;更多时候，算法性能并不是唯一指标。维护一个动态演化的系统，远比做出一个原型困难得多；正如《计算机程序的构造和解释》第一版前言所直言：“程序必须写得能够供人们阅读，偶尔地去供机器执行。”如果为了优化某个算法而显著牺牲整体可读性，甚至使代码对他人近乎不可读，那么取舍就必须回到团队状况与实际需求：我们不需要为了微小收益支付远超其价值的成本。这种情况虽然并不常见（因为多数时候不过是在沿用前人的算法及算法设计思想），但每一次优化仍都值得保持警惕。&lt;/p&gt;
&lt;p&gt;现实中的算法处理，并不像算法题那样理想化；优化目标也未必总是性能。正如实时系统首先关心的往往是用户体感，而不是吞吐量，更广泛的工程语境中，我们面对的其实是取舍与权衡。在算法题中，一题多解或许是一种训练思维的方式；但在现实中，选择哪一个解本身就是充分困难的挑战。由此会引出更深一层的问题：如果从架构视野审视，核心要义并不在于在一个理想化环境中识别模式，然后挑选自己掌握的算法将其解决。&lt;/p&gt;
&lt;p&gt;每个领域都有大量适配自身语境的算法，所谓最强、最高效的算法从来不存在。学习算法，不在于积累多少种算法以覆盖多少类问题，而在于把握算法设计模式：很多算法之所以频繁出现在算法竞赛中，并不一定因为它们最适合真实使用，而是因为它们最适合被组织成竞赛题。以红黑树为例，我日常使用它的频率绝对高于动态规划，毕竟每一次 &lt;code&gt;std::set&lt;/code&gt; 与 &lt;code&gt;std::map&lt;/code&gt; 的操作背后都是红黑树实现。当某个标准本身成为优化目标时，就尤其值得警惕；工业时代以来的标准化教育，实质上就是设立一个标准器件，再按照个体与标准器件的接近程度加以评估——倘若比拼的是这种针对某一类任务的泛化能力，我并不相信人类能够拼得过大语言模型。&lt;/p&gt;</content:encoded><category>算法学习</category><category>数据结构</category></item><item><title>AI选型的国产方案与开源替代</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-26-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-26-1-zh/</guid><description>没有绝对完美，只有具体语境中的最适宜；在现实约束与可能性边界之间起舞，正是计算机科学的魅力所在。</description><pubDate>Tue, 26 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;尽管 ChatGPT、Claude 或 Gemini 这类顶尖旗舰模型在许多场景中近乎不可用，并且企业或团队在现实部署中还必须面对严峻的合规约束，国产方案与开源替代并不因此失去意义。它们固然尚难抵达旗舰闭源模型的综合高度，却已经在相当多的任务中具备足够可用、且成本结构极具吸引力的实践价值。&lt;/p&gt;
&lt;p&gt;DeepSeek 发布了最新的 v3.1 版本，延续了此前五月更新 R1 权重却不更改版本号的低调作风。尽管这个 3.1 版本也被批评为 benchmark 表现漂亮、实际体验却不够稳定的“做题家”，但其低廉价格、对 reasoning 路线的祛魅，以及 speed always wins 的工程判断，仍然体现出团队对于前沿技术节奏的独特品味：问题与其说在于模型本身是否“全能”，不如说在于能否为它找到恰当的使用场景。就 API 调用而言，它应付日常需求与通用任务已经相当从容。&lt;/p&gt;
&lt;p&gt;如果目标是提升 coding 生产力，那么 Kimi K2 在 agentic abilities 上的亮眼表现，实际上会构成更具现实吸引力的选择。将 Claude Code 一类的 CLI 交互范式与 Kimi K2 结合，已经形成一种在价格与性能之间颇为稳健的折中方案；与此同时，Claude Pro subscription 的使用摩擦，以及它对于网络环境与支付环境的苛刻要求，也构成了事实上的准入壁垒。正如欧洲过度谨慎的安全政策一样，“宪法AI”有些时候也难免显得手脚受缚。&lt;/p&gt;
&lt;p&gt;国产开源模型的另一个重要坐标是 Qwen，它与 Gemini 一并被视为某种管理层面的奇迹；反观 Meta 的 LLaMA，则实在开了一个并不值得艳羡的先例。Qwen 的 embedding 模型长期以来都是相当可靠的选择；若要构建增强检索生成（RAG）系统，或面向更广泛任务搭建向量数据库，它们完全值得被纳入优先考量。&lt;/p&gt;
&lt;p&gt;另一个值得关注的方向是性能敏感型任务：OpenAI 发布了自己的开源模型 gpt-oss，其能力达到了 o4-mini 乃至 o3-mini 的水平。这一定位并不低；即便是更新权重后的 DeepSeek-R1-0528，与同为推理模型的 o4-mini 之间仍有距离，颇有一种事实上的“地板高于天花板”的意味。因此，对于性能需求明确的场景，购买并部署该开源模型的云服务，会是一条相当务实的路径。&lt;/p&gt;
&lt;p&gt;至于那些规模细微却真实存在的需求，Gemma 3n 则提供了另一种解法。这个 mobile-first 的开源模型近乎反常识：其极限压缩到不到 1B 参数量的版本，依然保留了多模态能力。对于移动计算与即时翻译而言，这几乎就是一份毫不含蓄的福音。&lt;/p&gt;
&lt;p&gt;总的来说，时间来到 2025 年 8 月，开源模型与闭源模型之争虽然早在去年便以一种直接而压倒性的方式告一段落，但这并不妨碍我们借助灵活的工程手腕与相对严谨的学术方法，重新构建属于自己的技术体系。比如 &lt;a href=&quot;https://arxiv.org/html/2505.19797v1&quot;&gt;The Avengers: A Simple Recipe for Uniting Smaller Language Models to Challenge Proprietary Giants&lt;/a&gt; 这篇文章，便通过聚类算法组织开源模型，取得了超越 GPT-4.1 的效果；其机制实际上近似于一种显式的 MoE 架构。它持续提醒我们所谓工程学的奥义：没有绝对完美，只有具体语境中的最适宜；在现实约束与可能性边界之间起舞，正是计算机科学的魅力所在。&lt;/p&gt;</content:encoded><category>模型选型</category><category>AI工程</category><category>开源模型</category></item><item><title>vibe coding：40 分钟锻造一个标准库气质的 stack</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-25-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-25-1-zh/</guid><description>GPT-5 在接口、类型约束与分配器语义上，确有几分标准库风范。</description><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GPT-5 的工程能力究竟抵达了何处？&lt;/p&gt;
&lt;p&gt;不作冗长铺陈，以下是我与它协作 40 分钟后得到的产物。&lt;/p&gt;
&lt;p&gt;代码原则上应以 GPLv3.0 发布；但我暂不便建立 GitHub 仓库并提供副本，因此许可证文本请见：&lt;a href=&quot;https://www.gnu.org/licenses/&quot;&gt;https://www.gnu.org/licenses/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;交互过程中的提示词如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;让我们实现一个 Stack 类，目标标准为 C++23。
1. 使用 template &amp;#x3C;typename... Types&gt; 配合 std::variant 和 std::visit 处理对齐与类型安全问题
2. 支持标准库风格的迭代器
3. 使用 std::construct_at 和 std::destroy_at 手动管理对象生命周期，避免不必要的开销
4. 兼容 pmr 和 allocator，支持修改分配策略
5. 使用 concept 明确界定相关需求，避免退化为模板天书
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;当前实现已经具备较好的基础，我们继续迭代：
1. 检查模板参数包展开相关的问题
[compiler log ...]
2. 除非确有必要，使用 `and`、`or` 等替代形式的逻辑运算符，避免老式 C 风格写法
3. 使用 Doxygen 风格注释，说明可以充分展开；注释信息采用中英双语
4. 面向日常使用，在合适位置设置默认参数，并尽可能使模板参数能够自行推导
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;先收束一下，进入 bug 修复：
[compiler log ...]
本轮更新聚焦如下几点：
1. 引入以 noexcept(move) 为粒度的更细异常保证
2. 提供 small_buffer 小对象优化版本
3. 在可行范围内加强 static reflection，对基础类型以及对齐条件可控的小类型进行优化
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;维持设计需求不变（亦即保留既有设计语义约束），继续修复 bug：
[compiler log ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;目前的结果相当可观。
接下来的任务：生成一份测试代码，对我们的 stack 进行近乎饱和的性质测试；全部写在 main 函数中。
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;测试代码仍存在问题，请修复该 bug：
[compiler log ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 azureveil &amp;#x3C;azureveil4295@yeah.net&gt;
//
// This file is part of azureveil stack demo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see &amp;#x3C;https://www.gnu.org/licenses/&gt;.

#include &amp;#x3C;variant&gt;
#include &amp;#x3C;memory&gt;
#include &amp;#x3C;memory_resource&gt;
#include &amp;#x3C;type_traits&gt;
#include &amp;#x3C;concepts&gt;
#include &amp;#x3C;iterator&gt;
#include &amp;#x3C;utility&gt;
#include &amp;#x3C;new&gt;
#include &amp;#x3C;cstddef&gt;
#include &amp;#x3C;stdexcept&gt;
#include &amp;#x3C;algorithm&gt;
#include &amp;#x3C;initializer_list&gt;
#include &amp;#x3C;array&gt;
#include &amp;#x3C;cstring&gt; // memcpy
#include &amp;#x3C;cassert&gt;

namespace tools
{
    /**
     * @brief A type-list for meta programming. / 用于元编程的类型列表
     */
    template &amp;#x3C;class... Ts&gt;
    struct type_list
    {
    };

    /* ========================= Concepts（概念）========================= */

    /**
     * @brief Check T is in pack Ts...  / 判断类型 T 是否在 Ts... 中
     */
    template &amp;#x3C;class T, class... Ts&gt;
    concept InPack = (std::same_as&amp;#x3C;std::remove_cvref_t&amp;#x3C;T&gt;, Ts&gt; or ...);

    /**
     * @brief Element pack requirements for stack. / 栈的候选类型包需求
     * - every type is destructible and move-constructible
     * - pack size &gt; 0
     *
     * 重要：折叠表达式必须写成 ((A and B) and ...)，避免编译器报
     * &quot;binary expression in operand of fold-expression&quot;。
     */
    template &amp;#x3C;class... Ts&gt;
    concept StackElementPack =
        ((std::destructible&amp;#x3C;Ts&gt; and std::move_constructible&amp;#x3C;Ts&gt;) and ...) and (sizeof...(Ts) &gt; 0);

    /**
     * @brief Allocator can be rebound to T. / 分配器可重绑定到 T
     */
    template &amp;#x3C;class Alloc, class T&gt;
    concept RebindableAllocator = requires {
        typename std::allocator_traits&amp;#x3C;Alloc&gt;::template rebind_alloc&amp;#x3C;T&gt;;
    };

    /**
     * @brief f is a valid visitor for std::variant&amp;#x3C;Ts...&gt;. / 访问器概念
     */
    template &amp;#x3C;class F, class... Ts&gt;
    concept VariantVisitor =
        requires(F f, std::variant&amp;#x3C;Ts...&gt; &amp;#x26;v) { std::visit(f, v); } and
        requires(F f, const std::variant&amp;#x3C;Ts...&gt; &amp;#x26;cv) { std::visit(f, cv); };

    /* ========================= meta: unique types ===================== */

    namespace detail
    {

        /**
         * @brief contains&amp;#x3C;List, T&gt; -&gt; bool_constant  / 判断 T 是否在 type_list 中
         */
        template &amp;#x3C;class List, class T&gt;
        struct contains;

        template &amp;#x3C;class T&gt;
        struct contains&amp;#x3C;type_list&amp;#x3C;&gt;, T&gt; : std::false_type
        {
        };

        template &amp;#x3C;class Head, class... Tail, class T&gt;
        struct contains&amp;#x3C;type_list&amp;#x3C;Head, Tail...&gt;, T&gt;
            : std::conditional_t&amp;#x3C;std::same_as&amp;#x3C;Head, T&gt;, std::true_type, contains&amp;#x3C;type_list&amp;#x3C;Tail...&gt;, T&gt;&gt;
        {
        };

        /**
         * @brief prepend&amp;#x3C;List, T&gt;  / 在列表前部插入
         */
        template &amp;#x3C;class List, class T&gt;
        struct prepend;

        template &amp;#x3C;class... Ts, class T&gt;
        struct prepend&amp;#x3C;type_list&amp;#x3C;Ts...&gt;, T&gt;
        {
            using type = type_list&amp;#x3C;T, Ts...&gt;;
        };

        /**
         * @brief unique_types&amp;#x3C;Ts...&gt; 去重并保持首次出现顺序 / make unique, keep first-appearance order
         */
        template &amp;#x3C;class... Ts&gt;
        struct unique_types;

        template &amp;#x3C;&gt;
        struct unique_types&amp;#x3C;&gt;
        {
            using type = type_list&amp;#x3C;&gt;;
        };

        template &amp;#x3C;class T, class... Rest&gt;
        struct unique_types&amp;#x3C;T, Rest...&gt;
        {
            using rest = typename unique_types&amp;#x3C;Rest...&gt;::type;
            static constexpr bool in = contains&amp;#x3C;rest, T&gt;::value;
            using type = std::conditional_t&amp;#x3C;in, rest, typename prepend&amp;#x3C;rest, T&gt;::type&gt;;
        };

        /**
         * @brief forward decl: to_basic_stack&amp;#x3C;Alloc, type_list&amp;#x3C;...&gt;&gt;
         * 仅声明，不定义，避免重定义冲突！
         */
        template &amp;#x3C;class Alloc, class List&gt;
        struct to_basic_stack;

    } // namespace detail

    /* ========================= basic_stack with SBO =================== */

    /**
     * @class basic_stack
     * @brief A type-safe, allocator-aware multi-type stack based on std::variant,
     *        with optional Small Buffer Optimization (SBO).
     *        使用 std::variant 的类型安全、分配器友好、可选小对象优化（SBO）的多类型栈。
     *
     * @tparam Allocator  allocator type (defaults to std::allocator&amp;#x3C;std::byte&gt;)
     *                    分配器类型（默认 std::allocator&amp;#x3C;std::byte&gt;）
     * @tparam SBO        inline capacity in elements for small-buffer optimization
     *                    小对象优化的内联容量（按元素个数）
     * @tparam Types...   element alternative types (non-empty pack)
     *                    备选元素类型（非空包）
     *
     * # Design highlights / 设计要点
     * - Storage is `std::variant&amp;#x3C;Types...&gt;` for type-safety &amp;#x26; alignment.（对齐与类型安全由 variant 保证）
     * - Manual lifetime with `std::construct_at` / `std::destroy_at`.（手动控制对象生命周期）
     * - Standard-style iterators (contiguous).（标准库风格迭代器，连续存储）
     * - Supports PMR 和常规分配器。/ Supports PMR and regular allocators.
     * - `noexcept(move)` granular policy: relocation chooses move-or-copy by traits. /
     *    基于类型性质（traits）选择“移动或拷贝”，提供细粒度异常保证。
     * - SBO: small sizes live on-stack; larger spill to heap. / 小容量走栈上内联，大容量溢出到堆。
     *
     * @note 对“静态反射（static reflection）”的可行优化：
     *  - 若 `storage_type`（即 `std::variant&amp;#x3C;Ts...&gt;`）满足
     *    `std::is_trivially_copyable_v`，则迁移使用 `memcpy` 快路径；
     *  - 否则若 `std::is_nothrow_move_constructible_v&amp;#x3C;storage_type&gt;`，则采用移动构造；
     *  - 否则退化为拷贝构造，保持强异常保证（strong exception safety）。
     */
    template &amp;#x3C;
        class Allocator = std::allocator&amp;#x3C;std::byte&gt;,
        std::size_t SBO = 0,
        class... Types&gt;
        requires StackElementPack&amp;#x3C;Types...&gt; and RebindableAllocator&amp;#x3C;Allocator, std::variant&amp;#x3C;Types...&gt;&gt;
    class basic_stack
    {
    public:
        using storage_type = std::variant&amp;#x3C;Types...&gt;;
        using value_type = storage_type;
        using allocator_type = typename std::allocator_traits&amp;#x3C;Allocator&gt;::template rebind_alloc&amp;#x3C;storage_type&gt;;
        using allocator_traits = std::allocator_traits&amp;#x3C;allocator_type&gt;;

        using size_type = std::size_t;
        using difference_type = std::ptrdiff_t;

        using reference = storage_type &amp;#x26;;
        using const_reference = const storage_type &amp;#x26;;
        using pointer = storage_type *;
        using const_pointer = const storage_type *;

        using iterator = storage_type *;
        using const_iterator = const storage_type *;
        using reverse_iterator = std::reverse_iterator&amp;#x3C;iterator&gt;;
        using const_reverse_iterator = std::reverse_iterator&amp;#x3C;const_iterator&gt;;

    private:
        // ---- Static reflection knobs / 静态性质开关 ----
        static constexpr bool triv_copyable = std::is_trivially_copyable_v&amp;#x3C;storage_type&gt;;
        static constexpr bool triv_destruct = std::is_trivially_destructible_v&amp;#x3C;storage_type&gt;;
        static constexpr bool nothrow_move = std::is_nothrow_move_constructible_v&amp;#x3C;storage_type&gt;;
        static constexpr std::size_t TSize = sizeof(storage_type);
        static constexpr std::size_t TAlign = alignof(storage_type);
        static constexpr bool has_sbo = (SBO &gt; 0);

        /// @brief SBO 内联块（仅在 has_sbo 为真时作为候选）/ inline storage when SBO enabled
        struct alignas(TAlign) sbo_block
        {
            std::array&amp;#x3C;std::byte, SBO * TSize&gt; buf{};
        };

        /// @brief 无 SBO 时的占位类型（空壳）/ empty placeholder when SBO disabled
        struct no_sbo_block
        {
        };

        /// @brief 依据 has_sbo 选择实际的 sbo_ 成员类型
        using sbo_storage_t = std::conditional_t&amp;#x3C;has_sbo, sbo_block, no_sbo_block&gt;;

    public:
        /* ------------- constructors / 构造与析构 ------------- */

        /**
         * @brief Default ctor (uses SBO if available, otherwise no allocation).
         *        默认构造（若启用 SBO 则指向内联缓冲，否则不分配）
         */
        basic_stack() noexcept(noexcept(allocator_type{}))
            : alloc_{}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()} {}

        /**
         * @brief Construct with allocator. / 指定分配器构造
         */
        explicit basic_stack(const Allocator &amp;#x26;a)
            : alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()} {}

        /**
         * @brief PMR resource ctor (enabled only for PMR allocator). /
         *        PMR 资源构造（仅当分配器为 PMR 时启用）
         */
        basic_stack(std::pmr::memory_resource *r)
            requires std::same_as&amp;#x3C;std::remove_cvref_t&amp;#x3C;Allocator&gt;, std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;&gt;
            : alloc_{allocator_type{r}}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
        {
        }

        /**
         * @brief Reserve capacity upfront. / 预留容量的构造
         * @param reserve_cap initial capacity / 初始容量
         * @param a allocator (optional) / 可选分配器
         */
        explicit basic_stack(size_type reserve_cap, const Allocator &amp;#x26;a = Allocator{})
            : alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
        {
            if (reserve_cap &gt; cap_)
                reserve(reserve_cap);
        }

        /**
         * @brief Construct from initializer_list of variants. / 从 variant 列表构造
         */
        basic_stack(std::initializer_list&amp;#x3C;storage_type&gt; init, const Allocator &amp;#x26;a = Allocator{})
            : alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
        {
            reserve(init.size());
            for (auto const &amp;#x26;v : init)
                push(v);
        }

        basic_stack(const basic_stack &amp;#x26;other)
            : alloc_{allocator_traits::select_on_container_copy_construction(other.alloc_)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
        {
            reserve(other.size_);
            try
            {
                uninit_copy_n_(other.data_, other.size_, data_);
                size_ = other.size_;
            }
            catch (...)
            {
                destroy_n_(data_, size_);
                deallocate_if_heap_();
                throw;
            }
        }

        basic_stack(basic_stack &amp;#x26;&amp;#x26;other) noexcept
            : alloc_{std::move(other.alloc_)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
        {
            // 优先：若分配器“总是相等”，可直接窃取缓冲
            if constexpr (allocator_traits::is_always_equal::value)
            {
                if (other.using_heap_())
                {
                    data_ = other.data_;
                    cap_ = other.cap_;
                    size_ = other.size_;
                    other.data_ = other.inline_ptr_();
                    other.cap_ = other.init_cap_();
                    other.size_ = 0;
                    return;
                }
            }
            // PMR 特例：同一 resource 也可安全窃取
            else if constexpr (std::same_as&amp;#x3C;std::remove_cvref_t&amp;#x3C;Allocator&gt;, std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;&gt;)
            {
                if (other.using_heap_() and alloc_.resource() == other.alloc_.resource())
                {
                    data_ = other.data_;
                    cap_ = other.cap_;
                    size_ = other.size_;
                    other.data_ = other.inline_ptr_();
                    other.cap_ = other.init_cap_();
                    other.size_ = 0;
                    return;
                }
            }
        }

        basic_stack &amp;#x26;operator=(const basic_stack &amp;#x26;other)
        {
            if (this == &amp;#x26;other)
                return *this;
            if constexpr (allocator_traits::propagate_on_container_copy_assignment::value)
            {
                if (alloc_ != other.alloc_)
                {
                    clear();
                    deallocate_if_heap_();
                }
                alloc_ = other.alloc_;
            }
            if (other.size_ &gt; cap_)
            {
                reallocate_to_(other.size_);
            }
            // 赋值已有元素并填充/销毁多余
            const size_type common = std::min(size_, other.size_);
            for (size_type i = 0; i &amp;#x3C; common; ++i)
                data_[i] = other.data_[i];
            if (size_ &amp;#x3C; other.size_)
            {
                uninit_copy_n_(other.data_ + size_, other.size_ - size_, data_ + size_);
            }
            else
            {
                destroy_n_(data_ + other.size_, size_ - other.size_);
            }
            size_ = other.size_;
            return *this;
        }

        basic_stack &amp;#x26;operator=(basic_stack &amp;#x26;&amp;#x26;other) noexcept(
            allocator_traits::propagate_on_container_move_assignment::value or
            std::is_nothrow_move_assignable_v&amp;#x3C;allocator_type&gt;)
        {
            if (this == &amp;#x26;other)
                return *this;
            if constexpr (allocator_traits::propagate_on_container_move_assignment::value)
            {
                clear();
                deallocate_if_heap_();
                alloc_ = std::move(other.alloc_);
                if (other.using_heap_())
                {
                    data_ = other.data_;
                    size_ = other.size_;
                    cap_ = other.cap_;
                    other.data_ = other.inline_ptr_();
                    other.size_ = 0;
                    other.cap_ = other.init_cap_();
                }
                else
                {
                    data_ = inline_ptr_();
                    cap_ = init_cap_();
                    size_ = 0;
                    reserve(other.size_);
                    uninit_move_or_copy_n_(other.data_, other.size_, data_);
                    size_ = other.size_;
                    other.clear();
                }
            }
            else
            {
                if (alloc_ == other.alloc_)
                {
                    clear();
                    deallocate_if_heap_();
                    if (other.using_heap_())
                    {
                        data_ = other.data_;
                        size_ = other.size_;
                        cap_ = other.cap_;
                        other.data_ = other.inline_ptr_();
                        other.size_ = 0;
                        other.cap_ = other.init_cap_();
                    }
                    else
                    {
                        data_ = inline_ptr_();
                        cap_ = init_cap_();
                        size_ = 0;
                        reserve(other.size_);
                        uninit_move_or_copy_n_(other.data_, other.size_, data_);
                        size_ = other.size_;
                        other.clear();
                    }
                }
                else
                {
                    assign_move_from_(other);
                }
            }
            return *this;
        }

        ~basic_stack()
        {
            destroy_n_(data_, size_);
            deallocate_if_heap_();
        }

        /* ------------- iterators / 迭代器 ------------- */

        /// @brief begin / 起始迭代器
        iterator begin() noexcept { return data_; }
        const_iterator begin() const noexcept { return data_; }
        const_iterator cbegin() const noexcept { return data_; }

        /// @brief end / 末尾迭代器
        iterator end() noexcept { return data_ + size_; }
        const_iterator end() const noexcept { return data_ + size_; }
        const_iterator cend() const noexcept { return data_ + size_; }

        /// @brief reverse begin / 反向起始
        reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
        const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{end()}; }
        const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{end()}; }

        /// @brief reverse end / 反向末尾
        reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
        const_reverse_iterator rend() const noexcept { return const_reverse_iterator{begin()}; }
        const_reverse_iterator crend() const noexcept { return const_reverse_iterator{begin()}; }

        /* ------------- capacity / 容量 ------------- */

        /// @brief empty? / 是否为空
        [[nodiscard]] bool empty() const noexcept { return size_ == 0; }

        /// @brief size / 当前大小
        [[nodiscard]] size_type size() const noexcept { return size_; }

        /// @brief capacity / 当前容量
        [[nodiscard]] size_type capacity() const noexcept { return cap_; }

        /// @brief reserve capacity / 预留容量
        void reserve(size_type new_cap)
        {
            if (new_cap &amp;#x3C;= cap_)
                return;
            reallocate_to_(new_cap);
        }

        /// @brief shrink to fit (may move back into SBO) / 收缩容量（可能回迁至 SBO）
        void shrink_to_fit()
        {
            if (size_ == cap_)
                return;
            const size_type target = size_;
            // 若启用 SBO 且可容纳，则回迁内联
            if constexpr (has_sbo)
            {
                if (target &amp;#x3C;= SBO)
                {
                    reallocate_to_(SBO);
                    return;
                }
            }
            reallocate_to_(target);
        }

        /// @brief clear (destroy all elements) / 清空（析构所有元素）
        void clear() noexcept
        {
            destroy_n_(data_, size_);
            size_ = 0;
        }

        /* ------------- element access / 访问 ------------- */

        /// @brief top (mutable) / 顶部元素（可变）
        reference top()
        {
            if (empty())
                throw std::out_of_range(&quot;stack is empty&quot;);
            return data_[size_ - 1];
        }
        /// @brief top (const) / 顶部元素（常量）
        const_reference top() const
        {
            if (empty())
                throw std::out_of_range(&quot;stack is empty&quot;);
            return data_[size_ - 1];
        }

        /**
         * @brief Visit top via visitor. / 使用访问器访问顶部元素
         */
        template &amp;#x3C;VariantVisitor&amp;#x3C;Types...&gt; F&gt;
        decltype(auto) visit_top(F &amp;#x26;&amp;#x26;f)
        {
            return std::visit(std::forward&amp;#x3C;F&gt;(f), top());
        }
        template &amp;#x3C;VariantVisitor&amp;#x3C;Types...&gt; F&gt;
        decltype(auto) visit_top(F &amp;#x26;&amp;#x26;f) const
        {
            return std::visit(std::forward&amp;#x3C;F&gt;(f), top());
        }

        /**
         * @brief Visit all elements. / 访问所有元素
         */
        template &amp;#x3C;VariantVisitor&amp;#x3C;Types...&gt; F&gt;
        void visit_all(F &amp;#x26;&amp;#x26;f)
        {
            for (auto &amp;#x26;e : *this)
                std::visit(f, e);
        }
        template &amp;#x3C;VariantVisitor&amp;#x3C;Types...&gt; F&gt;
        void visit_all(F &amp;#x26;&amp;#x26;f) const
        {
            for (auto &amp;#x26;e : *this)
                std::visit(f, e);
        }

        /* ------------- modifiers / 修改器 ------------- */

        /**
         * @brief Push a variant value. / 压入一个 variant 值
         */
        void push(value_type v)
        {
            ensure_growth_();
            std::construct_at(data_ + size_, std::move(v));
            ++size_;
        }

        /**
         * @brief Push a concrete alternative in Types... / 压入具体候选类型的值
         */
        template &amp;#x3C;class T&gt;
            requires InPack&amp;#x3C;T, Types...&gt;
        void push(T &amp;#x26;&amp;#x26;x)
        {
            ensure_growth_();
            std::construct_at(
                data_ + size_,
                std::in_place_type&amp;#x3C;std::remove_cvref_t&amp;#x3C;T&gt;&gt;,
                std::forward&amp;#x3C;T&gt;(x));
            ++size_;
        }

        /**
         * @brief Emplace the top element as alternative T. / 原地构造指定候选类型 T 的顶部元素
         */
        template &amp;#x3C;class T, class... Args&gt;
            requires InPack&amp;#x3C;T, Types...&gt; and std::constructible_from&amp;#x3C;T, Args...&gt;
        reference emplace(Args &amp;#x26;&amp;#x26;...args)
        {
            ensure_growth_();
            std::construct_at(data_ + size_, std::in_place_type&amp;#x3C;T&gt;, std::forward&amp;#x3C;Args&gt;(args)...);
            ++size_;
            return data_[size_ - 1];
        }

        /**
         * @brief Pop the top element (destroy). / 弹出顶部元素（析构）
         */
        void pop()
        {
            if (empty())
                throw std::out_of_range(&quot;stack is empty&quot;);
            std::destroy_at(data_ + (size_ - 1));
            --size_;
        }

        /* ------------- allocator / 分配器 ------------- */

        /// @brief get allocator / 获取分配器
        allocator_type get_allocator() const noexcept { return alloc_; }

        /**
         * @brief Rebind to another allocator type. / 迁移到另一种分配器策略
         * @note Copy/move variant elements as needed. / 根据异常安全选择拷贝或移动
         */
        template &amp;#x3C;class NewAlloc&gt;
            requires RebindableAllocator&amp;#x3C;NewAlloc, storage_type&gt;
        basic_stack&amp;#x3C;NewAlloc, SBO, Types...&gt; rebind_allocator(const NewAlloc &amp;#x26;na = NewAlloc{}) const
        {
            basic_stack&amp;#x3C;NewAlloc, SBO, Types...&gt; dst(na);
            dst.reserve(size_);
            for (size_type i = 0; i &amp;#x3C; size_; ++i)
            {
                dst.push(data_[i]);
            }
            return dst;
        }

    private:
        /* ------------- internal helpers / 内部工具 ------------- */

        // —— SBO helpers / SBO 辅助 —— //
        pointer inline_ptr_() noexcept
        {
            if constexpr (has_sbo)
            {
                // std::launder: 避免潜在的严格别名与生命周期问题
                return std::launder(reinterpret_cast&amp;#x3C;pointer&gt;(sbo_.buf.data()));
            }
            else
            {
                return nullptr;
            }
        }
        const_pointer inline_ptr_() const noexcept
        {
            if constexpr (has_sbo)
            {
                return std::launder(reinterpret_cast&amp;#x3C;const_pointer&gt;(sbo_.buf.data()));
            }
            else
            {
                return nullptr;
            }
        }
        bool using_heap_() const noexcept
        {
            if constexpr (has_sbo)
                return data_ != inline_ptr_();
            else
                return data_ != nullptr;
        }
        pointer init_data_ptr_() noexcept
        {
            if constexpr (has_sbo)
                return inline_ptr_();
            else
                return nullptr;
        }
        size_type init_cap_() const noexcept
        {
            if constexpr (has_sbo)
                return SBO;
            else
                return 0;
        }

        static allocator_type rebind_alloc_(const Allocator &amp;#x26;a)
        {
            using rebound = typename std::allocator_traits&amp;#x3C;Allocator&gt;::template rebind_alloc&amp;#x3C;storage_type&gt;;
            return rebound{a};
        }

        pointer allocate_(size_type n) { return allocator_traits::allocate(alloc_, n); }

        void deallocate_if_heap_() noexcept
        {
            if (using_heap_() and data_)
                allocator_traits::deallocate(alloc_, data_, cap_);
            data_ = init_data_ptr_();
            cap_ = init_cap_();
        }

        static void destroy_n_(pointer p, size_type n) noexcept
        {
            for (size_type i = 0; i &amp;#x3C; n; ++i)
            {
                std::destroy_at(p + i);
            }
        }

        static void uninit_copy_n_(const_pointer src, size_type n, pointer dst)
        {
            size_type i = 0;
            try
            {
                for (; i &amp;#x3C; n; ++i)
                    std::construct_at(dst + i, src[i]);
            }
            catch (...)
            {
                destroy_n_(dst, i);
                throw;
            }
        }

        // —— granular move/copy by traits / 基于性质的精细迁移 —— //
        static void uninit_move_or_copy_n_(pointer src, size_type n, pointer dst)
        {
            // Fast path: trivially copyable → memcpy
            if constexpr (triv_copyable)
            {
                std::memcpy(static_cast&amp;#x3C;void *&gt;(dst), static_cast&amp;#x3C;void const *&gt;(src), n * TSize);
                return;
            }
            size_type i = 0;
            try
            {
                if constexpr (nothrow_move)
                {
                    for (; i &amp;#x3C; n; ++i)
                        std::construct_at(dst + i, std::move(src[i]));
                }
                else
                {
                    for (; i &amp;#x3C; n; ++i)
                        std::construct_at(dst + i, src[i]); // fallback copy
                }
            }
            catch (...)
            {
                destroy_n_(dst, i);
                throw;
            }
        }

        void reallocate_to_(size_type new_cap)
        {
            // 选择目标缓冲区：优先使用 SBO（若可容纳）
            pointer new_buf = nullptr;
            if constexpr (has_sbo)
            {
                new_buf = (new_cap &amp;#x3C;= SBO) ? inline_ptr_() : allocate_(new_cap);
            }
            else
            {
                new_buf = allocate_(new_cap);
            }

            if (new_buf == data_)
            { // 仍在同一缓冲，无需动作
                cap_ = (new_buf == inline_ptr_()) ? init_cap_() : new_cap;
                return;
            }

            size_type moved = 0;
            try
            {
                uninit_move_or_copy_n_(data_, size_, new_buf);
                moved = size_;
            }
            catch (...)
            {
                if (new_buf != inline_ptr_())
                {
                    destroy_n_(new_buf, moved); // 对于 memcpy 情况，trivial dtor 则无副作用
                    allocator_traits::deallocate(alloc_, new_buf, new_cap);
                }
                throw;
            }
            // 旧缓冲区清理
            destroy_n_(data_, size_);
            if (using_heap_())
                allocator_traits::deallocate(alloc_, data_, cap_);

            data_ = new_buf;
            cap_ = (data_ == inline_ptr_()) ? init_cap_() : new_cap;
        }

        void ensure_growth_()
        {
            if (size_ == cap_)
            {
                const size_type base = (cap_ == 0) ? (has_sbo ? SBO : 0) : cap_;
                const size_type new_cap = (base == 0) ? 4 : (base * 2);
                reallocate_to_(new_cap);
            }
        }

        void assign_move_from_(basic_stack &amp;#x26;other)
        {
            clear();
            reserve(other.size_);
            uninit_move_or_copy_n_(other.data_, other.size_, data_);
            size_ = other.size_;
            other.clear();
        }

    private:
        allocator_type alloc_{};
        pointer data_{nullptr};
        size_type size_{0};
        size_type cap_{0};
        // SBO storage (exists only if SBO&gt;0) / 仅当 SBO&gt;0 才有意义
        sbo_storage_t sbo_{};
    };

    /* ========================= Aliases / 别名 ========================= */

    template &amp;#x3C;class... Types&gt;
    using Stack = basic_stack&amp;#x3C;std::allocator&amp;#x3C;std::byte&gt;, 0, Types...&gt;;

    template &amp;#x3C;std::size_t SBO, class... Types&gt;
    using SmallStack = basic_stack&amp;#x3C;std::allocator&amp;#x3C;std::byte&gt;, SBO, Types...&gt;;

    template &amp;#x3C;class... Types&gt;
    using PmrStack = basic_stack&amp;#x3C;std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;, 0, Types...&gt;;

    template &amp;#x3C;std::size_t SBO, class... Types&gt;
    using PmrSmallStack = basic_stack&amp;#x3C;std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;, SBO, Types...&gt;;

    /* === meta glue: materialize to_basic_stack (after basic_stack exists) === */
    /* ！！！修复重定义：只在此处做唯一的偏特化定义，之前仅声明不定义。 */

    namespace detail
    {
        template &amp;#x3C;class Alloc, class... Ts&gt;
        struct to_basic_stack&amp;#x3C;Alloc, type_list&amp;#x3C;Ts...&gt;&gt;
        {
            using type = basic_stack&amp;#x3C;Alloc, 0, Ts...&gt;;
        };
    } // namespace detail

    /* ========================= Factory helpers / 工厂辅助 ==================== */
    /**
     * @brief Make a stack with deduced unique types from arguments (default allocator).
     *        通过实参类型推导并去重，构造使用默认分配器的栈。
     *
     * @return stack with Types... = unique(remove_cvref_t&amp;#x3C;Args&gt;...)  / 去重后的类型包
     *
     * 使用示例：
     *   auto s = tools::stack_of(1, std::string{&quot;x&quot;}, 3.14); // Stack&amp;#x3C;int, std::string, double&gt;
     */
    template &amp;#x3C;class... Args&gt;
    auto stack_of(Args &amp;#x26;&amp;#x26;...)
    {
        using uniq_list = typename detail::unique_types&amp;#x3C;std::remove_cvref_t&amp;#x3C;Args&gt;...&gt;::type;
        using stack_t = typename detail::to_basic_stack&amp;#x3C;std::allocator&amp;#x3C;std::byte&gt;, uniq_list&gt;::type;
        return stack_t{};
    }

    /**
     * @brief Make a stack with a given allocator. / 使用给定分配器的工厂
     */
    template &amp;#x3C;class Alloc, class... Args&gt;
        requires RebindableAllocator&amp;#x3C;Alloc, std::variant&amp;#x3C;std::remove_cvref_t&amp;#x3C;Args&gt;...&gt;&gt;
    auto stack_of_with_alloc(const Alloc &amp;#x26;a, Args &amp;#x26;&amp;#x26;...)
    {
        using uniq_list = typename detail::unique_types&amp;#x3C;std::remove_cvref_t&amp;#x3C;Args&gt;...&gt;::type;
        using stack_t = typename detail::to_basic_stack&amp;#x3C;Alloc, uniq_list&gt;::type;
        return stack_t{a};
    }

    /**
     * @brief PMR convenience factory. / PMR 快捷工厂
     */
    template &amp;#x3C;class... Args&gt;
    auto stack_of_pmr(std::pmr::memory_resource *r, Args &amp;#x26;&amp;#x26;...)
    {
        using uniq_list = typename detail::unique_types&amp;#x3C;std::remove_cvref_t&amp;#x3C;Args&gt;...&gt;::type;
        using stack_t = typename detail::to_basic_stack&amp;#x3C;std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;, uniq_list&gt;::type;
        return stack_t{r};
    }

    /* ========================= CTAD guides / 推导指引 ======================== */
    /**
     * @brief Deduce Ts... from initializer_list&amp;#x3C;variant&amp;#x3C;Ts...&gt;&gt; (default allocator).
     *        从 initializer_list&amp;#x3C;variant&amp;#x3C;Ts...&gt;&gt; 推导元素类型（默认分配器）
     */
    template &amp;#x3C;class... Ts&gt;
    basic_stack(std::initializer_list&amp;#x3C;std::variant&amp;#x3C;Ts...&gt;&gt;, std::allocator&amp;#x3C;std::byte&gt;)
        -&gt; basic_stack&amp;#x3C;std::allocator&amp;#x3C;std::byte&gt;, 0, Ts...&gt;;
    template &amp;#x3C;class... Ts&gt;
    basic_stack(std::initializer_list&amp;#x3C;std::variant&amp;#x3C;Ts...&gt;&gt;)
        -&gt; basic_stack&amp;#x3C;std::allocator&amp;#x3C;std::byte&gt;, 0, Ts...&gt;;

    /**
     * @brief Deduce Ts... + PMR allocator from initializer_list. / 从初始化列表推导 Ts... 与 PMR 分配器
     */
    template &amp;#x3C;class... Ts&gt;
    basic_stack(std::initializer_list&amp;#x3C;std::variant&amp;#x3C;Ts...&gt;&gt;, std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;)
        -&gt; basic_stack&amp;#x3C;std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;, 0, Ts...&gt;;

} // namespace tools
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 azureveil &amp;#x3C;azureveil4295@yeah.net&gt;
//
// This file is part of azureveil stack demo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see &amp;#x3C;https://www.gnu.org/licenses/&gt;.

#include &quot;stack.hpp&quot;

#include &amp;#x3C;cassert&gt;
#include &amp;#x3C;string&gt;
#include &amp;#x3C;iostream&gt;
#include &amp;#x3C;variant&gt;
#include &amp;#x3C;vector&gt;
#include &amp;#x3C;array&gt;
#include &amp;#x3C;memory_resource&gt;
#include &amp;#x3C;algorithm&gt;
#include &amp;#x3C;stdexcept&gt;
#include &amp;#x3C;cctype&gt;
#include &amp;#x3C;type_traits&gt;
#include &amp;#x3C;climits&gt;

// —— 两个测试类型必须放在函数外：标准禁止局部类含静态数据成员 ——

// 计数型：用于验证生命周期（构造/析构数量）
struct Counted
{
    static int live;
    int id = -1;
    Counted() : id(-1) { ++live; }
    explicit Counted(int i) : id(i) { ++live; }
    Counted(const Counted &amp;#x26;o) : id(o.id) { ++live; }
    Counted(Counted &amp;#x26;&amp;#x26;o) noexcept : id(o.id) { ++live; }
    Counted &amp;#x26;operator=(const Counted &amp;#x26;) = default;
    Counted &amp;#x26;operator=(Counted &amp;#x26;&amp;#x26;) = default;
    ~Counted() { --live; }
};
int Counted::live = 0;

// 用于异常路径：拷贝到第 N 次时抛异常；移动构造非 noexcept，促使“回退拷贝路径”
struct ThrowOnCopy
{
    static int copy_count;
    static int throw_after;
    int v = 0;
    ThrowOnCopy() = default;
    explicit ThrowOnCopy(int x) : v(x) {}
    ThrowOnCopy(const ThrowOnCopy &amp;#x26;o) : v(o.v)
    {
        if (++copy_count == throw_after)
            throw std::runtime_error(&quot;copy boom&quot;);
    }
    ThrowOnCopy(ThrowOnCopy &amp;#x26;&amp;#x26;o) noexcept(false) : v(o.v) {}
    ThrowOnCopy &amp;#x26;operator=(const ThrowOnCopy &amp;#x26;) = default;
    ThrowOnCopy &amp;#x26;operator=(ThrowOnCopy &amp;#x26;&amp;#x26;) = default;
    ~ThrowOnCopy() = default;
};
int ThrowOnCopy::copy_count = 0;
int ThrowOnCopy::throw_after = INT_MAX;

int main()
{
    using namespace tools;

    // ========== 1) 基础功能：SmallStack + 多类型 push/emplace/visit ==========
    {
        SmallStack&amp;#x3C;4, int, std::string, double, Counted&gt; s; // SBO=4
        assert(s.empty());
        s.emplace&amp;#x3C;int&gt;(42);
        s.push(std::string{&quot;alpha&quot;});
        s.push(3.14);
        s.emplace&amp;#x3C;Counted&gt;(7);

        assert(s.size() == 4);
        assert(s.capacity() &gt;= 4);
        s.visit_top([](auto const &amp;#x26;x)
                    {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T, Counted&gt;) assert(x.id == 7); });

        int cnt_int = 0;
        int cnt_str = 0;
        int cnt_dbl = 0;
        int cnt_cnt = 0;
        long long sum_int = 0;
        double sum_dbl = 0.0;
        s.visit_all([&amp;#x26;](auto const &amp;#x26;x)
                    {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T, int&gt;) { ++cnt_int; sum_int += x; }
            else if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) { ++cnt_str; }
            else if constexpr (std::is_same_v&amp;#x3C;T, double&gt;) { ++cnt_dbl; sum_dbl += x; }
            else if constexpr (std::is_same_v&amp;#x3C;T, Counted&gt;) { ++cnt_cnt; } });
        assert(cnt_int == 1 and cnt_str == 1 and cnt_dbl == 1 and cnt_cnt == 1);
        assert(sum_int == 42);
        assert(sum_dbl &gt; 3.0 and sum_dbl &amp;#x3C; 3.2);

        int live_before = Counted::live;
        s.pop(); // 弹出 Counted{7}
        assert(Counted::live == live_before - 1);
        assert(s.size() == 3);
        s.clear();
        assert(s.empty());
    }

    // ========== 2) 容量行为：SBO 命中、溢出堆、shrink 回迁 ==========
    {
        SmallStack&amp;#x3C;4, int, std::string&gt; s;
        for (int i = 0; i &amp;#x3C; 4; ++i)
            s.push(i); // SBO 内
        assert(s.capacity() == 4);

        s.push(100); // 触发溢出
        assert(s.capacity() &gt;= 5);

        while (s.size() &gt; 4)
            s.pop(); // 回到 SBO 大小
        s.shrink_to_fit();
        assert(s.capacity() == 4); // 回迁成功
    }

    // ========== 3) reserve/growth：多次重分配不丢数据 ==========
    {
        Stack&amp;#x3C;int, std::string&gt; s;
        for (int i = 0; i &amp;#x3C; 100; ++i)
        {
            if (i % 10 == 0)
                s.push(std::string{&quot;s-&quot; + std::to_string(i)});
            else
                s.push(i);
        }
        int ints = 0, strs = 0, sum = 0;
        s.visit_all([&amp;#x26;](auto const &amp;#x26;x)
                    {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T, int&gt;) { ++ints; sum += x; }
            else if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) { ++strs; } });
        assert(ints == 90 and strs == 10);
        assert(s.size() == 100);
        const auto sz = s.size();
        s.shrink_to_fit();
        assert(s.size() == sz);
    }

    // ========== 4) PMR：单调缓冲资源 + PmrSmallStack ==========
    {
        std::array&amp;#x3C;std::byte, 4096&gt; buf{};
        std::pmr::monotonic_buffer_resource mbr(buf.data(), buf.size());
        PmrSmallStack&amp;#x3C;4, int, std::string&gt; ps(&amp;#x26;mbr);
        ps.emplace&amp;#x3C;int&gt;(1);
        ps.emplace&amp;#x3C;std::string&gt;(&quot;pmr&quot;);
        ps.emplace&amp;#x3C;int&gt;(2);
        ps.emplace&amp;#x3C;int&gt;(3); // 填满 SBO
        ps.emplace&amp;#x3C;int&gt;(4); // 溢出到堆（仍由 mbr 管）
        assert(ps.size() == 5);
        int sum = 0, strings = 0;
        ps.visit_all([&amp;#x26;](auto const &amp;#x26;x)
                     {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T, int&gt;) sum += x;
            else if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) ++strings; });
        assert(sum == (1 + 2 + 3 + 4) and strings == 1);
    }

    // ========== 5) 工厂函数与 CTAD（不再用严格顺序的 static_assert） ==========
    {
        auto s1 = stack_of(1, std::string{&quot;x&quot;}, 2.5, 3, 4.0);
        using stor1 = typename decltype(s1)::storage_type;
        static_assert(std::variant_size_v&amp;#x3C;stor1&gt; == 3);
        // 运行期验证三种类型都能正常压入/访问
        s1.push(123);
        s1.push(std::string{&quot;ok&quot;});
        s1.push(3.14);
        bool saw_int = false, saw_str = false, saw_dbl = false;
        s1.visit_all([&amp;#x26;](auto const &amp;#x26;x)
                     {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T,int&gt;) saw_int = true;
            if constexpr (std::is_same_v&amp;#x3C;T,std::string&gt;) saw_str = true;
            if constexpr (std::is_same_v&amp;#x3C;T,double&gt;) saw_dbl = true; });
        assert(saw_int and saw_str and saw_dbl);

        // PMR 工厂 &amp;#x26; initializer_list + CTAD
        std::pmr::monotonic_buffer_resource mbr;
        auto s2 = stack_of_pmr(&amp;#x26;mbr, 1, std::string{&quot;y&quot;}, 3.0);
        using stor2 = typename decltype(s2)::storage_type;
        static_assert(std::variant_size_v&amp;#x3C;stor2&gt; == 3);

        using V = std::variant&amp;#x3C;int, std::string&gt;;
        basic_stack lst = {V{1}, V{std::string{&quot;ctad&quot;}}}; // Ts... 推导为 &amp;#x3C;int, std::string&gt;
        assert(lst.size() == 2);
    }

    // ========== 6) rebind_allocator：迁移到 PMR ==========
    {
        auto s = stack_of(1, std::string{&quot;reb&quot;}, 2.0);
        s.push(7);
        s.push(std::string{&quot;z&quot;});
        std::array&amp;#x3C;std::byte, 2048&gt; buf{};
        std::pmr::monotonic_buffer_resource mbr(buf.data(), buf.size());
        auto sp = s.rebind_allocator(std::pmr::polymorphic_allocator&amp;#x3C;std::byte&gt;{&amp;#x26;mbr});
        assert(sp.size() == s.size());

        auto stringify = [](auto const &amp;#x26;v) -&gt; std::string
        {
            return std::visit([](auto const &amp;#x26;x) -&gt; std::string
                              {
                using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
                if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) return x;
                else return std::to_string(x); }, v);
        };

        std::vector&amp;#x3C;std::string&gt; a, b;
        a.reserve(s.size());
        b.reserve(sp.size());
        for (auto const &amp;#x26;e : s)
            a.push_back(stringify(e));
        for (auto const &amp;#x26;e : sp)
            b.push_back(stringify(e));
        assert(a == b);
    }

    // ========== 7) 变更访问：就地变换字符串 ==========
    {
        SmallStack&amp;#x3C;4, int, std::string&gt; s;
        s.push(1);
        s.push(std::string{&quot;MiXed&quot;});
        s.push(2);
        for (auto &amp;#x26;v : s)
        {
            std::visit([](auto &amp;#x26;x)
                       {
                using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
                if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) {
                    for (auto&amp;#x26; c : x) c = static_cast&amp;#x3C;char&gt;(std::toupper(static_cast&amp;#x3C;unsigned char&gt;(c)));
                } }, v);
        }
        bool seen_upper = false;
        s.visit_all([&amp;#x26;](auto const &amp;#x26;x)
                    {
            using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
            if constexpr (std::is_same_v&amp;#x3C;T, std::string&gt;) seen_upper = (x == &quot;MIXED&quot;); });
        assert(seen_upper);
    }

    // ========== 8) 异常安全（强保证）：拷贝途中抛异常，栈保持不变 ==========
    {
        using S = Stack&amp;#x3C;ThrowOnCopy, int&gt;;
        S s;
        for (int i = 0; i &amp;#x3C; 8; ++i)
        {
            if (i % 2 == 0)
                s.push(ThrowOnCopy{i});
            else
                s.push(i);
        }
        const std::vector&amp;#x3C;int&gt; snapshot = [&amp;#x26;]
        {
            std::vector&amp;#x3C;int&gt; vv;
            vv.reserve(s.size());
            for (auto const &amp;#x26;e : s)
            {
                std::visit([&amp;#x26;](auto const &amp;#x26;x)
                           {
                    using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
                    if constexpr (std::is_same_v&amp;#x3C;T, int&gt;) vv.push_back(x);
                    else vv.push_back(x.v); }, e);
            }
            return vv;
        }();

        ThrowOnCopy::copy_count = 0;
        ThrowOnCopy::throw_after = 3;

        const auto old_size = s.size();
        const auto old_cap = s.capacity();
        bool threw = false;
        try
        {
            s.reserve(old_cap + 8); // 触发重分配 → 拷贝途中抛异常
        }
        catch (std::runtime_error const &amp;#x26;)
        {
            threw = true;
        }
        ThrowOnCopy::throw_after = INT_MAX;

        assert(threw);
        assert(s.size() == old_size); // 强保证：大小不变
        std::vector&amp;#x3C;int&gt; after;
        after.reserve(s.size());
        for (auto const &amp;#x26;e : s)
        {
            std::visit([&amp;#x26;](auto const &amp;#x26;x)
                       {
                using T = std::decay_t&amp;#x3C;decltype(x)&gt;;
                if constexpr (std::is_same_v&amp;#x3C;T, int&gt;) after.push_back(x);
                else after.push_back(x.v); }, e);
        }
        assert(after == snapshot);
    }

    // ========== 9) 生命周期泄漏检查 ==========
    {
        int before = Counted::live;
        {
            SmallStack&amp;#x3C;4, Counted&gt; s;
            for (int i = 0; i &amp;#x3C; 16; ++i)
                s.emplace&amp;#x3C;Counted&gt;(i);
            assert(Counted::live &gt;= before + 16);
            s.clear();
            assert(s.empty());
        }
        assert(Counted::live == before);
    }

    // ========== 10) 顶部访问与异常边界 ==========
    {
        Stack&amp;#x3C;int&gt; s;
        bool threw = false;
        try
        {
            (void)s.top();
        }
        catch (std::out_of_range const &amp;#x26;)
        {
            threw = true;
        }
        assert(threw);
        s.push(7);
        assert(std::get&amp;#x3C;int&gt;(s.top()) == 7);
        s.pop();
        assert(s.empty());
    }

    std::cout &amp;#x3C;&amp;#x3C; &quot;[OK] all saturated tests passed.\n&quot;;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>随性编程</category><category>数据结构</category></item><item><title>为什么 AI 赋能不明显？</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-25-2-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-25-2-zh/</guid><description>AI 已经是一项具有显著门槛的技术，早已不再是玩具式或学生式的实践。</description><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;为什么你或许会觉得 AI 的赋能效果并不显著，仿佛仍未真正把握所谓新质生产力？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;能稳定连接 AI tools；IP、网络环境与稳定账号，已经足以筛掉无数人。&lt;/li&gt;
&lt;li&gt;能开通 Plus；付费意愿、付费能力与付费渠道，又构成第二道现实门槛。&lt;/li&gt;
&lt;li&gt;能辨析模型选型和功能边界；面对繁复的模型矩阵（现有 8 个模型选项和 6 个功能选项），以及 memories、Projects、reference all chats、connectors 等功能，足以让无数人止步于界面之前。&lt;/li&gt;
&lt;li&gt;能进行提示工程；customize models、project instructions，以及每次对话中的上下文管理，实际上都是对表达、结构和任务分解能力的持续考验。&lt;/li&gt;
&lt;li&gt;能内化使用 AI 的习惯；把“先问 AI”变成问题处理的第一反应，用 AI 介入生活、学术与工程问题，并积累足够长时段的对话资产，这件事本身就难倒了无数人。&lt;/li&gt;
&lt;li&gt;能提出好问题；学术品味、工程直觉与自身认知结构，决定了 AI 能否真正成为放大器，而不是更高级的搜索框。&lt;/li&gt;
&lt;li&gt;能构建自己的 AI 应用；本地部署、API 调用与超参数、自建应用、增强检索生成、框架选择，这些环节共同构成了从使用者到建造者的分水岭。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我只能说，AI 已经是一项具有显著门槛的技术，早已不再是玩具式或学生式的实践。用好了之后，可以达到“博士之下无敌手”的效果，这并非夸张；“PhD-level AI”的说法或许仍有修辞成分，但当人叠加 AI 的超级杠杆，情形就彻底不同了。&lt;/p&gt;</content:encoded><category>AI-native</category></item><item><title>C++ 中的 Concept：把代数证明交还给编译器</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-25-3-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-25-3-zh/</guid><description>Concept 并不是给模板换一套体面语法，而是在向编译器递交代数结构的信息。</description><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;题外话，提到鸭子类型，就很难不想到 C++ 的 &lt;code&gt;concept&lt;/code&gt;。作为一种强静态类型语言，C++ 的类型系统长期以来都可以被理解为一种集合建模：声明一个类的字段，是在声明集合中元素的构成，实际上近似于给出一个元组；声明函数、方法和重载运算符，则是在定义作用于这些集合之上的算子。古典时代区分过程与函数，但我个人认为，过程本质上也是在程序状态，或某个局部状态之上作映射，所以不强行区分反倒相当合理。甚至说，它还不是多重集建模：在 C++ 的对象模型中，不同的完整对象通常必须能够由地址加以区分；空对象也至少占一个字节。趣事是，早年我看到用 &lt;code&gt;private&lt;/code&gt; 继承实现 has-a 关系时，总觉得这属于绕路的矫饰，后来才知道空基类优化（EBO）的意义：省下那一个字节，进而避免一串对齐赤字，确实很香。C++ 极具辨识度的模板机制，在 &lt;code&gt;static reflection&lt;/code&gt;、类型推导、编译期运算等神乎其技的用法之前，其本意毕竟是泛型；而泛型在更抽象的层面上，就是从一类过程里抽取代数过程。这就要求程序员自行检查：自己所定义的集合，是否满足该过程所需的代数性质。那套显学说，写代码，尤其是等待编译通过的时候，其实是在完成一种构造性证明；好比你必须证明某个集合满足域公理，才有资格调用域的性质。古早时期，也就是 &lt;code&gt;concept&lt;/code&gt; 之前，维护代数结构的责任主要交给程序员的心智；编译器则近乎冷酷地实例化、推导，然后在失败时吐出不可读的模板天书。如今有了 &lt;code&gt;concept&lt;/code&gt;，本质上是在向编译器提供代数结构的信息：编译器可以先在定义层面自动证明或证伪相关命题，这对人的认知负荷是一种极大的释放。许多手法，比如 &lt;code&gt;restrict&lt;/code&gt; 一类扩展性限定、&lt;code&gt;volatile&lt;/code&gt; 关键字和 attributes 这类语言特性，实际上也都是在向编译器补充信息，使局面不至于继续滑向不可控；所谓语义约束，说到底就是在改写信息的边界。&lt;/p&gt;</content:encoded><category>现代 C++</category><category>类型系统</category></item><item><title>C/C++ 的细微差异</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-22-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-22-1-zh/</guid><description>虽然大多数情况下我们会把 C 视作 C++ 的 subset，但二者之间仍有一些不应被忽略的细微差异。</description><pubDate>Fri, 22 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;虽然大多数情况下我们会把 C 视作 C++ 的 subset，但二者之间仍有一些不应被忽略的细微差异。顺便一提，我确实是先学 C，再学 C++ 的；这多少会影响我观察二者边界的方式。&lt;/p&gt;
&lt;p&gt;C++ 支持函数重载，这使得它在汇编层面的函数名经常呈现出近乎反人类的形态，也因此在 C++ 中调用 C 函数时需要加上 &lt;code&gt;extern &quot;C&quot;&lt;/code&gt;，否则链接器无法按 C 的符号名完成解析。以我最常用的 GCC 为例，对于一个 &lt;code&gt;int compute(int a, int b)&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int compute(int a, int b)
{
    return a + b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 C++ 中，汇编结果会得到：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;_Z7computeii:
.LFB0:
    pushq    %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe    %rbp, 0
    .seh_endprologue
    movl    %ecx, 16(%rbp)
    movl    %edx, 24(%rbp)
    movl    16(%rbp), %edx
    movl    24(%rbp), %eax
    addl    %edx, %eax
    popq    %rbp
    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而在 C 中则是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;compute:
    pushq    %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe    %rbp, 0
    .seh_endprologue
    movl    %ecx, 16(%rbp)
    movl    %edx, 24(%rbp)
    movl    16(%rbp), %edx
    movl    24(%rbp), %eax
    addl    %edx, %eax
    popq    %rbp
    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同一个 &lt;code&gt;int compute(int a, int b)&lt;/code&gt;，在 C++ 中会被改写为 &lt;code&gt;_Z7computeii&lt;/code&gt;，而在 C 中则仍是直接的 &lt;code&gt;compute&lt;/code&gt;。这与语言标准本身并不完全等价，而是编译器在实现命名空间与重载机制时进行的隐式重命名，也就是所谓 name mangling。它使 C++ 在相当程度上脱离了“高级汇编”的范畴，使用 &lt;code&gt;asm&lt;/code&gt; 嵌入时也不再像 C 那样近乎赤手空拳。&lt;/p&gt;
&lt;p&gt;另一个由语言功能引入的问题是虚表。
例如对于：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;struct test1
{
    virtual int compute(void)
    {
        return 1;
    }
};
 
struct expanded_test1 : public test1
{
    int compute(void) override
    {
        return 2;
    }
};
 
int main(void)
{
    expanded_test1 object;
    test1 &amp;#x26;reference = object;
    reference.compute();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;汇编中会出现：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;main:
.LFB2:
    subq    $56, %rsp
    .seh_stackalloc    56
    .seh_endprologue
    call    __main
    leaq    16+_ZTV14expanded_test1(%rip), %rax
    movq    %rax, 40(%rsp)
    movl    $0, %eax
    addq    $56, %rsp
    ret
    .seh_endproc
    .globl    _ZTS5test1
    .section    .rdata$_ZTS5test1,&quot;dr&quot;
    .linkonce same_size
_ZTS5test1:
    .ascii &quot;5test1\0&quot;
    .globl    _ZTI5test1
    .section    .rdata$_ZTI5test1,&quot;dr&quot;
    .linkonce same_size
    .align 8
_ZTS14expanded_test1:
    .ascii &quot;14expanded_test1\0&quot;
    .globl    _ZTI14expanded_test1
    .section    .rdata$_ZTI14expanded_test1,&quot;dr&quot;
    .linkonce same_size
    .align 8
_ZTI14expanded_test1:
    .quad    _ZTVN10__cxxabiv120__si_class_type_infoE+16
    .quad    _ZTS14expanded_test1
    .quad    _ZTI5test1
    .globl    _ZTV14expanded_test1
    .section    .rdata$_ZTV14expanded_test1,&quot;dr&quot;
    .linkonce same_size
    .align 8
_ZTV14expanded_test1:
    .quad    0
    .quad    _ZTI14expanded_test1
    .quad    _ZN14expanded_test17computeEv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 &lt;code&gt;_ZTV14expanded_test1&lt;/code&gt; 指向虚表，因此 C 风格的 &lt;code&gt;memset(this, 0, sizeof(T))&lt;/code&gt; 会破坏对象的虚表布局，并制造极其隐蔽的错误。于是，在 C++ 中更可靠的初始化方式应当是初始化列表，或者在构造函数中进行成员赋值。另一个值得玩味的细节是，在启用 RTTI 的情况下（这一机制如今已是标准语言设施的一部分），编译器还会提供 &lt;code&gt;_ZTVN10__cxxabiv120__si_class_type_infoE+16&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;还有一些更细碎、但同样容易刺破抽象幻觉的差异，例如 NULL。
在 &lt;code&gt;stdlib.h&lt;/code&gt; 中可以看到它是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#define NULL ((void *)0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但在 C++ 中它变成了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#define NULL 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此，如果在模板推导时使用 &lt;code&gt;NULL&lt;/code&gt;，它会被视作一个 &lt;code&gt;int&lt;/code&gt; 类型，而不是指针。也正因为如此，在 C++ 中应当使用语言内置的 &lt;code&gt;nullptr&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;另一个值得展开的问题，是 C 与 C++ 在内存分配模型上的差异；它绝不能被简化为从 &lt;code&gt;malloc/free&lt;/code&gt; 到 &lt;code&gt;new/delete&lt;/code&gt; 的语法替换。C++ 的 &lt;code&gt;new&lt;/code&gt; 和 &lt;code&gt;delete&lt;/code&gt; 的关键之处在于，它们会自动调用构造函数与析构函数，而这可能引入相当隐蔽的高开销。假设存在一个相对复杂的类型 &lt;code&gt;T&lt;/code&gt;，如果写下 &lt;code&gt;new T[100];&lt;/code&gt;，程序会在分配阶段直接调用 100 次构造函数，而不是等到对象真正被使用时才为其付费；在散列表这类装填因子通常较低、因而存在大量未使用槽位的场景中，这种预先构造几乎就是纯粹的浪费。更 modern 的做法是用 &lt;code&gt;pmr&lt;/code&gt; 解耦内存分配策略，或者搭配 &lt;code&gt;allocator&lt;/code&gt;，在需要使用对象时调用 &lt;code&gt;std::construct_at&lt;/code&gt;，不再需要时再手动销毁；定位 &lt;code&gt;new&lt;/code&gt; 运算符，即 placement new，也能达到类似效果，但前者显然更符合现代 C++ 的表达方式。鉴于 &lt;code&gt;malloc/free&lt;/code&gt; 或者 &lt;code&gt;new/delete&lt;/code&gt; 都是在全局堆上分配内存，并发场景下往往需要加锁，从而造成事实上的串行化，解耦内存分配策略一直是高性能计算的刚需。&lt;/p&gt;
&lt;p&gt;总的来说，若从当下的底层开发实践回望 C，常见形态往往是 “C and assembly” 的结合：C 作为高级语言，在许多位置节省了机械性劳动，同时维持了效率，并且与汇编之间保持着更好的亲和性，因此适合 Linux 内核这类需要精细控制的场合。同样地，如果项目中已经使用汇编，C++ 会带来事实上的多语言异构代码；而要良好实现一个 C++ 编译器，也远比实现一个 C 编译器折磨得多，毕竟 C++ 的模板系统从 C++98 开始就已经是图灵完备的。由此看，C++ 引入面向对象、lambda、&lt;code&gt;template&lt;/code&gt; 等特性，更适合高性能软件开发中的另一条路线：零成本抽象，是底层性能与高层架构既要又要之后留下的妥协，也是野心。&lt;/p&gt;</content:encoded><category>C/C++</category><category>汇编</category></item><item><title>CS 入门的核心素质：热情与品味</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-20-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-20-1-zh/</guid><description>CS 入门的关键并非神秘的天赋，而是 passion 与 taste 在方法论、元认知和制度环境中的生成。</description><pubDate>Wed, 20 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我个人认为，入门学习 CS 的核心素质只有两个：passion and taste。这并非为了显得锋利而刻意简化，而是对诸多经验作高度归纳之后得到的结论。&lt;/p&gt;
&lt;p&gt;有些人或许会谈论一个人适不适合学计算机、有没有所谓“计算机天赋”。但在我看来，“天赋”这一归因更多时候只是把暂时解释不了的差异赋予一种不可证伪的神圣性，以换取心理上的安慰。即使是天赋异禀的少年，我们也仍然能够感受到他与老成学者之间的距离；因此，将天赋与积累粗暴地设置为二元对立，并不能真正解释这种鸿沟。&lt;/p&gt;
&lt;p&gt;我们常常因为某个人能够举一反三、一点即通，便说他“天赋高”。事实上，我们真正看到的是一种把握事物本质的能力；而这种能力，很大程度上是抽象思维训练的结果。否则，哲学和数学何以发挥如此巨大的威力？有些计算机人认为数学并不关键，并不是因为 engineering 与 formal 之间存在彻底断裂，而是因为他们没有选择以数学的方式理解计算机：他们或许只看到了数学作为工具的一面，例如算法分析的工具、算法背后的图论原理，却没有选择用集合去建模类型、用代数去理解数据库查询。不过，我并不认为这构成一个必须纠正的问题；数学也并非品味的唯一道路。按照我个人的理念：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any formal system or architecture is a perspective-constrained reification of reality, whose utility is measured not by perfection, but by relevance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这也解释了前文所说的鸿沟与洞察力的来源：我们是否通过刻意练习，将一套方法论与认识论层面的思维工具内化为自己的思考方式？这种内化会生成隐性知识；而我们所谓的 taste，实际上正是借由这些隐性知识审视事物的结果。许多“找个 mentor 带”的建议，本质上也是对品味的追寻：通过观察与模仿，捕捉那些难以被完全显性化的模式。&lt;/p&gt;
&lt;p&gt;沿着对“品味”的分析继续推进，我们可以得到一个颇具反直觉性的发现：taste 也可以是自顶向下的。它可以从简洁的公理出发，经由高度理论驱动的路径展开，并让哲学思辨与自我反思相互校正。与其完全依赖对行为和模式的经验归纳，不如借助分析哲学提供的工具，让经验归纳与演绎推导彼此配合。何况，阅读本身就使人得以把握来自他人的经验；否则，文献阅读的意义又何在？这便是元认知的核心意义。&lt;/p&gt;
&lt;p&gt;不过，“天才都来自刻意练习”这一模型虽然具有相当好的解释力，用它考察现实却仍然令人不尽满意：如果一切差异都可以在刻意练习中消解，那么现实中的参差难道只能被归结为个体能动性的差异？这里自然需要进行否定之否定：这种对现实的建模固然解释了认知如何来源于对方法论和认识论的刻意练习，却没有解释元认知从何而来。对于一个人最初如何认识到某种模式，它选择了沉默。&lt;/p&gt;
&lt;p&gt;如果站在一个更广阔的视角上看，最初的元认知几乎带有彻底的随机性：是否遇到好的家庭，是否遇到好的导师，是否拥有独特的成长经历与人生境遇。许多人所谓“事教人”的认知，也可以在这里得到统一；而前文恰好指出了这种成长模式的低效所在。不过，环境与制度的变量也因此得到了更好的诠释：我们或许不能预设某个个体必然具备足够的能动性，但社会可以为那些对知识怀抱憧憬与热情的人提供更多试错机会，消解更多隐性知识门槛，至少提高他们获得启发、获得最初那一下推力的概率。所以才会说，创新，尤其是创造性破坏，不能被计划出来，而只能在一个条件足够充分的环境中自然发生。&lt;/p&gt;
&lt;p&gt;至此，我们可以获得一个更高阶的理解：天才是训练的产物，但训练路径本身往往依赖环境与制度所施加的推力；而元认知能力，又可以极大地帮助人超越这种推力。因此我才会立论：对于 CS 入门或初级阶段而言，并不存在什么唯一且必然的路径，但有两个不可绕开的要素：passion 与 taste。某种标准化路径或许能让人至少参与到社会分工之中；然而，那种只关心打了多少分、只崇拜取得分数之方法论的制式产品，显然无法造就真正的天赋。说到底，品味太差。&lt;/p&gt;</content:encoded><category>CS</category><category>学习方法</category><category>元认知</category></item><item><title>CS 半山腰：反平均化的学习辩护</title><link>https://atelier.moesegfault.dev/zh/blog/2025-08-08-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-08-08-1-zh/</guid><description>在抽象、推导与冷板凳之间，重新理解计算机科学的半山腰。</description><pubDate>Fri, 08 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在一位朋友的文章里读到一段话，颇有触动。大意是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;《数据库系统概念》中有大量关系代数式的数学推导，《算法导论》也是近乎同一种鬼样子；根本学不完，自己还在啃，考试却已经逼近，而且这些东西似乎在较初级的开发工作里也派不上多少用场。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我对此实在太有共鸣。第一次接触概率论与数理统计时，我也是从概率公理起手，一路推导并搭建整个体系。知识对我而言天然就是网状的；但这份结构感的代价，是要学的东西总是太多。&lt;/p&gt;
&lt;p&gt;今天回来看值得吗？&lt;/p&gt;
&lt;p&gt;我想说：爽得要死，而且爽得并不廉价。追逐事物本质的第一性原理思考，起源于我学习数学的经验；在演绎与归纳的交汇处，我接触到正反合的辩证法；带着批判性思维大量阅读经典，又将我推向对反身性的追寻。至于那种网状的知识结构，则让我逐渐扎根于系统思维，而不是被零散技巧牵着鼻子走。&lt;/p&gt;
&lt;p&gt;大多数时候，这条路并不缺乏反馈。无论是搭建起复杂系统时由创造行为内生出的成就感，还是以非常之道成非常之事后由他人赞赏输入的认可，都是我成长路上不可或缺的一部分。当然，沿着曲折的小路向上攀登，难免有坐冷板凳的凄凉感；更别提同道之上往往会遇见高手，乃至令人高山仰止。半山腰的人迹固然稀少了许多，但它所给出的视野，也远比山脚广阔。&lt;/p&gt;
&lt;p&gt;所以，这不是什么完美主义，也不是什么贪婪或强迫症；它只是对计算机科学之极致的追求，以及一种近乎顽固的纯粹热爱。工业时代以来，那种流水线般追求平均值的教育想象，原本就很难理解我们的志向。如果超越某个既定范围、向着知识疆界的边缘进发也要被称作贪婪，那实在是对古来先贤的一种贬损。&lt;/p&gt;</content:encoded><category>CS</category><category>学习方法</category><category>知识论</category></item><item><title>竞赛导向的计算机教育，并非真正的 CS</title><link>https://atelier.moesegfault.dev/zh/blog/2025-06-07-1-zh/</link><guid isPermaLink="true">https://atelier.moesegfault.dev/zh/blog/2025-06-07-1-zh/</guid><description>做一只在计算机科学中自由飞翔的小鸟，不也是很好吗？</description><pubDate>Sat, 07 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我个人认为，计算机科学何时开始学习都无不可，它更多依赖个人兴趣的驱动，以及脚踏实地的求索。为什么在所谓“不恰当的时机”学习计算机会留下“只会做题”“缺乏想象力”的印象？我以为，这首先是教育与评价体系的责任。&lt;/p&gt;
&lt;p&gt;中国普遍意义上的计算机教育，尤其在本科以前，是高度竞赛导向的；甚至不妨说，本科以前真正被承认为“学习计算机”的人，几乎只有信息学竞赛的参与者。由这种竞赛导向衍生出的“算法”标签化，后来依然四处蔓延；如果说算法岗之所以令人艳羡，其经济逻辑在于稀缺性带来的高薪，那么从另一个切面看，其中也沉积着算法竞赛塑造出来的精英倾向。“鄙视链”的阴影斑驳陆离，却又心照不宣，清晰可见。&lt;/p&gt;
&lt;p&gt;在这样的环境中做计算机教育，竞赛几乎成了投放精力的唯一目标，算法题与激烈角逐也就无从避免。不过，算法竞赛所训练出的能力，并不必然能够移植为工程能力：算法竞赛追求的是逼近理论极限，工程则更青睐稳定性、可拓展性、可复用性等指标。诚如先前戏言：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“性能极致的代码只是上古神话时代的传说，大量可堪一用的代码才是决胜之道！”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里限于篇幅不作证明，仅取一个简单例子说明：线段树是算法竞赛中的基础结构，然而在《算法导论》正文中亦难觅其踪迹；红黑树普遍到 C++ 标准库直接提供支持，我以前身边的同学却未必比熟悉线段树更熟悉它。我无意评判竞赛圈的风气，因为我并不甚熟悉；不过，与我们分析高考、考研时的思路类似，只要存在某种评价标准、某条固定赛道，就难免围绕它发明创造出一整套知识体系。倘若从这里出发，关于计算机科学的元认知自然会大相径庭。&lt;/p&gt;
&lt;p&gt;这样看，小时候“不适合学计算机”，并非计算机科学本身的性质使然，而是中国并没有为计算机教育提供一个足够普适的方案。这种落后处处可见：比如数学教育中对先进计算工具的排斥，比如直到疫情前都还甚嚣尘上的对互联网的偏见。对个人而言，解决方案也十分简单，却难以做到：只要不在这个框架下思考问题，自然不会遭遇这个框架所制造的问题。&lt;/p&gt;
&lt;p&gt;计算机科学的自学传统一直是显学，本来也没有规定知识必须从何处取得；如《儒林外史》所言，“资格困人，贤豪同叹”：各种“资格”是做题家的掌上明珠，却也是有志者的绊脚石。告别这些稀奇古怪的人为设限，海阔天空自然并非奢望。&lt;/p&gt;
&lt;p&gt;做一只在计算机科学中自由飞翔的小鸟，不也是很好吗？&lt;/p&gt;</content:encoded><category>CS</category><category>计算机教育</category><category>学习心得</category></item></channel></rss>