本文使用 macbook pro m4 + vscode 编写, 编译器是 g++-15 + glibc++, 键盘是 ibm model m 的 ssk 版本但是使用了自己打印的主板和 cherry 青轴.

本文讲了实际的 c++26 和 我希望的 c++26 之间的关系.

不太妙啊 不太妙 没想到啊 没想到
别再问我了 我不知道 结局总是那么可笑
所以刷空间儿 的照片儿 越看越 湿眼圈儿
该说的话到嘴边儿 真不如过来抱一下
到底是怎么了? 不知道该怎么去说
千言万语都抵不过 积在心里的那一刻
就这样 故事又重新回到了开头
可能因为身在其中 总能感觉特别陌生
后知后觉 渐行渐远 不约而同 没结没完
那一段儿 那一片儿 那些天 那张脸
当能力成为了勇气 我的榜样变成了兄弟
我梦到了现在的你 仿佛像是看见我自己
有一天冰雪会融化 伤口会变成伤疤

我非常反对的 iso c++26 提案特性

如果所有语言都在使劲浑身解数想要变成 rust, 那设计这个语言本来的目的是什么呢?
java 是为了替代 c++, 而 java 发明了一次编译到处运行的 jvm.
go 是为了替代 c++, 而 go 有通讯和可以比肩 python 的标准库.
rust 是为了替代 c++, 而 rust 搞出的是借用和可变借用.
我从来没说过 rust 不好, 我承认我用 rust 写过 opengl.
java 没有被 rust 同质化, java 依然到处都是空引用.
go 也没有被 rust 同质化, go 依然在用 java 发明的垃圾回收.
如果 c++ 变成了用 c 语法写的 rust, 那么 c++ 还有什么存在的意义吗? 难道是为了让 c 程序员过渡到 rust 中间有个缓冲吗?
我不是说 k&r 的设计优秀, 但我觉得作为包括了 java 和 go 甚至 c# 和 python 在内的 c like 语言, 危险当然是有危险的价值的.
可以是速度, 可以是自由度, 可以是任何东西, 重要的是危险本身可以带来价值.

hazard 指针

从提案设计来看, 很显然是 son of graydon.

upd: c++26 给这玩意留下了, 那好吧, 我不用就好了.

[[unsafe]]

无论无论如何, 你认为的危险操作可能是我需要的.

而且这会导致使用了这些危险操作的 c++23 代码无法通过 c++26 的编译, 这违背了 c++ 的 较新的 c++ 标准应尽量兼容较旧标准的合法代码 原则.

另外, [[unsafe]] 的含义是 "除了这里之外都是安全的", 但实际上 c++ 语言本身应该全是 unsafe, 所以应该加 [[safe]] 来表示 "除了这里之外都是危险的", 所以 [[unsafe]] 是 graydon 的政变.

upd: c++26 真的给它删了.

我想要额外加入的

bjarne 提出的 profile

我坚决支持 bjarne 提出的所有思想 (注意我没说我反对所有 bjarne 反对的), bjarne 是为了避免 ub 那我跟.

消除 ub 是消除 ub, 不是变的安全. 消除 ub 不会变的安全, 消除 ub 也能提高开发效率和运行效率. 我始终认为 c++ 是个危险的语言, 但是总有办法能写出来安全的代码.

upd: c++26 没加这个, /ll

contracts

本来是在 c++26 提案里的, 听说即将被删除了, 希望能加入吧.

使用编译指令 -fcontract-semantic=quick_enforce 可以让代码 fail fast.

fail fast: 见 lil_tea c++ style guide.

upd: c++26 通过了这个, 可喜可贺.

prof. stroustrup 认为这个既不最小也不可行, 但我觉得这个的作用还是我可以把 pre(cond) 当成 if (!cond) std::abort() 的简单写法, 也就不用在函数前面检查每个参数了.

restrict

restrict 是个优秀的设计, 你向编译器承诺 我发誓我不会传入两个相同的东西, 这样编译器就可以放心的优化了.

我要说的是, 我想要 restrict 支持任何的除了传值之外的传参方式.

比如说, 我这样写:


void mul(matrix &s, const matrix &x, const matrix &y) {
// 假装我在这里实现了矩阵乘法
}

那么如果 s 和 x 是同一个对象 (很正常的 s∗=ys∗=y 语义), 那么这里是会导致错误的 (x 一边被 s 修改, 一边还要用乘积给 s 赋值, 简直就是胡闹).

所以我加上这个:


void mul(matrix &restrict s, const matrix &restrict x, const matrix &restrict y) {
// 假装我在这里实现了矩阵乘法
}

然后让编译器帮我检查, 如果对象重叠就直接报错.

同样的, 也可以支持 stl 和迭代器, 也可以支持智能指针, 只要重叠就让编译器报错.

当然了, 这很明显是违背了最初的含义是用于优化代码, 但这确实可以让代码更加安全, 优化与否我确实不在意, 因为我相信 -O2 的优化能力.

其实 contracts 能完整满足 restrict 的要求, 只是运行时和编译时的区别而已, 而且还支持更多的判断方式.

int main(const std::span<std::string_view> &args)

这个的含义是非常显然的, 就是 java 学来的 public static void main(String[] args), 用了 c++ 写法.

在继续允许 int main(int argv, char **argv) 和 int main(void) 的情况下, 再有一个现代化的版本也是非常优秀的.

注意: java 的 args[0] 已经是参数, 而我们根据 c 惯例让 args[0] 表示调用路径, 后面的 args[1] 到 args[args.size() - 1] 才是参数.

超级基类

有一个类, 我不想让它被实例化, 也不想让它被用于传参 (包括不允许引用和指针), 但有很多类都要继承它, 那么我就把这个类写成超级基类. 超级基类可以有虚函数, 可以有纯虚函数, 也可以没有虚函数, 可以继承一个超级基类, 只要不实例化且不做参数就行.


baseclass entity {
std::vector<double> pos_;
public:
entity(std::size_t d)
: pos_(d) {}
std::expected<void, std::string> move(const std::vector<double> &velocity, long time) {
if (velocity.size() != pos_.size())
std::unexpected<std::string>("纬度错误");
if (time < 0)
std::unexpected<std::string>("时间错误");
for (std::size_t x : std::views::iota(0uz, pos_.size()))
pos_[x] += velocity[x] * time;
}
const std::vector<double>& where(void) const {
return pos_;
}
};
class player : entity;
class zombie : entity;
baseclass npc : entity;
class merchant : npc;

额外的, 如果你让一个超级基类继承一个普通类, 那我不会拦着你, 但你应该想清楚你要做什么再

更多推荐