乐趣区

对现代C++的一点看法

背景
逛水木社区 C ++ 版块,看到了一篇很有意思的帖子 –《C++20 会变得陌生》。楼主贴出了分别用 C ++11 和 C++20 编写的代码,如下:
void cpp_11() {
std::vector<int> v{1, 2, 3, 4, 5};

std::vector<int> even;
std::copy_if(v.begin(), v.end(), std::back_inserter(even),
[](int i) {return i % 2 == 0;});

std::vector<int> results;
std::transform(even.begin(), even.end(),
std::back_inserter(even),
[](int i) {return i * 2;});

for (int n : results) std::cout << n << ‘ ‘;
}

void cpp_20() {
std::vector<int> v{1, 2, 3, 4, 5};
v | ranges::view::filter([](int i) {return i % 2 == 0;})
| ranges::view::transform([](int i) {return i * 2;})
| ranges::view::foreach([](int i) {std::cout << i << ‘ ‘;});
}
以上代码实现了数据操作:

从向量 v 里筛选出偶数的元素
将以上得到的每个元素分别乘以 2
将以上得到的每个元素分别打印出来

C++11 通过使用 algorithm 里的几个函数按步骤实现;而 C ++20 通过使用新的 ranges 扩展实现了相同的效果,通过使用 | 将数据连接了起来,类似于 Unix shell 的管道。
我猜作者的意思应该是想表达 C ++20 变化大,会让一些人感到陌生,但下面的评论就很有意思了,大体有以下几类:1.“颜值派”:这语法可真丑 2.“保守派”:C++ 在函数式编程上路越来越作死 3.“逃离派”:建议使用 Rust 或 Go 替代越来越臃肿的 C ++4.“现实派”:C++ 真是越来越复杂了,连语法都看不懂,哪一天会撑爆 5.“理性派”:C++ 的一些痛点必须解决,而目前看来也只能这样解决
几点看法
近几年,C++ 的演进进入了快车道,之前 C ++98 到 C ++11,历时 13 年;现在 C ++11、C++14、C++17、C++20,每 3 年一版,带来了改进的同时,也引起了众多的吐槽,比如上面的评论。现在是我的一些看法,为了避免发散,针对上面的代码做讨论。
关于语法
美丑永远是相对的,得看你跟谁比。比如,如果简洁是一种美,那跟纯函数式语言 Haskell 比,肯定要丑了,下面是 Haskell 等价的代码,很明显要简洁的多。
[1,2,3,4,5] & filter even & map (*2) & mapM_ print
跟 C 语言比呢?如果抽象是一种美,我觉得 C ++ 要美观的多,比如 C 中只能通过循环来实现遍历,而现代 C ++ 中已经有了很多结构控制函数,比如 foreach、filter 等,这种函数望文生义,你很明白就能知道代码在干什么,而循环语句则需要你进到循环内部才能明白在做什么。我是从 C 转向 C ++ 的,起初写 C ++ 代码的时候,根本就不用引用、namespace 这些东东,而是用指针和命名前缀代替,当时也觉得不美。久而久之发现,指针传参的时候要判断指针是否为空,这样的代码真的很丑,那么长的标识符也很丑,而引用和名字空间恰好解决了这些问题,反倒是觉得好看了。
C++20 跟 C++11 比呢?C++ 20 美,因为上文的代码既简洁,又清晰了表达了代码意图。
C++ 语法是有巨大的历史包袱的,想进步很难,但 C ++ 在语法上面是在进步的,比如使用 {} 统一初始化语句,虽然初看起来比较别扭,但一旦统一了,代码看起来是比较漂亮的。代码一致性也是美的一种。
关于函数式
函数式编程已经成了编程语言的必备特性,不光 C ++ 引入了,其他语言,比如 Java 等等都包含了某些函数式语言的特性。从某种程度上来说,函数式编程是趋势。比如,匿名函数在某些情况下是很方便的,避免每次都要去定义一些仅用一次的函数。
C++20 可能要引入的 concepts 也来源于函数式编程语言,包括 Rust 的 trait,都跟 haskell 的 typeclass 有关,一旦编译器支持了,对模板编程绝对是一大利好。
C++ 既然是多泛型语言,再多支持一个函数式,也就不奇怪了。
关于 C ++ 的替代语言
这方面,Rust 和 Go 的呼声最高。在我看来,C++ 不会被替代,但 Rust 是一种比 C ++ 更好的选择,而 Go 可能是 Java 的替代。
简要说下两门语言的对比:Rust 和 Go 的语言设计是两个方向,前者从学术化出发,兼顾实用化,后者从实用化出发,慢慢引入其他高级特性。1. 从整体上看,Rust 语言要完整,后期出现坑的可能性要小,Go 通过简单吸引了大量的开发者,在 1.0 版泛型都没有,而且异常处理极其单薄。从语言一致性上看,Rust 较好。2. 从语言效率看,Rust 跟 C ++ 都没有 GC,速度相当;Go 有 Gc,速度是优于 Java 的,但离 C ++ 还有一段距离,而且这个距离很难再减了。
有一点要提,Go 入门实在是太快了,稍微看一下语法,在 ide 里就可以写代码了;而 Rust 的所有权、borrow 机制,尤其是生命期就拦住了不少人。
如果没有特殊需求,优选新语言,因为新语言历史包袱少,能剪掉很多学习成本。
关于 C ++ 的体量
这几年,C++ 加快了演进速度,越来越多的特性加入到了语言当中,相对地,C++ 做减法的速度却没快起来。这导致了一种结果,就是 C ++ 语法膨胀的太快了。而且 C ++ 还要兼容 C 语言,最后语言规范刷刷的涨。
关于对策,语言的标准我们是没有可操纵空间的,但我们可以选择自己的“个人标准”。如,你是做应用开发的,用到底层特性的可能性不大,这个时候,你就完全可以不用原始指针、不用宏、仅使用 {} 初始化。。。这样是完全可以的。
总结
现代 C ++ 是在进步的。后续文章开始写一些现代 C ++ 的语言或库的特性了,大部分都是老工具很好的替代。
请关注我的公众号哦。

退出移动版