V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zxCoder
V2EX  ›  程序员

虽然说没有银弹,但是我感觉 c++完全可以设计成更友好,开发速度更快的语言

  •  
  •   zxCoder · 2021-07-14 21:14:58 +08:00 · 17453 次点击
    这是一个创建于 1232 天前的主题,其中的信息可能已经有所发展或是发生改变。

    c++大佬别杠我,不可否认的是 c++学起来实在太难,写起来实在太慢

    实际上很多东西感觉完全可以反过来吸收别的语言的优点

    209 条回复    2023-10-16 14:43:54 +08:00
    1  2  3  
    wctml
        101
    wctml  
       2021-07-16 11:03:44 +08:00
    使用 C++就是浪费生命,我已经浪费了 5 年了。
    ipwx
        102
    ipwx  
       2021-07-16 11:15:40 +08:00   ❤️ 2
    @levelworm 在任何你想用 new T 并且只有一个人拥有它的地方都能用 unique_ptr 。

    至于 shared_ptr,主要是不特定多个持有者要共享同一个对象才会用,这些持有者到底谁先死都不知道的时候还是有必要的。在多线程并发程序里面有时候还是躲不过的,比如 boost::asio
    INCerry
        103
    INCerry  
       2021-07-16 11:16:12 +08:00
    @dqzcwxb 这个特性确实很吸引人 虽然造成了吞吐量下降 和 更多的内存占用
    ipwx
        104
    ipwx  
       2021-07-16 11:17:39 +08:00   ❤️ 1
    @ysc3839 @levelworm 另外我觉得 shared_ptr 最合适的场景其实是只读对象或者数据对象。一个对象被创建出来,记录了一些信息,发给多个持有者,这些持有者的生命周期都不一样,并且在错综复杂的多线程环境中。这种时候用 shared_ptr 就很合适。

    比如进程里面通过消息队列来传递消息的这种场景。
    ipwx
        105
    ipwx  
       2021-07-16 11:20:05 +08:00
    @junkun const char* 可以隐式转换为 bool 这个还真让我踩过。不过一个函数会同时在同样的位置接受这两个类型的参数还真挺少见。
    wutiantong
        106
    wutiantong  
       2021-07-16 11:31:31 +08:00   ❤️ 1
    @Austaras
    可以看出你非常喜欢 rust,我虽然没学过 rust 不过坦率来说,我挺抵触“在语言层面上显式地定义生命周期”这个 idea 的。
    对象的生命周期在 C++里明明已经不是问题了,而这与智能指针没有太大关系,主要是受益于移动语义和 RAII 。
    现在在 C++里,对象在定义(不是 new )时诞生,在离开 scope 时自动消亡,并不需要付出多少精力去解决所谓的生命周期问题。
    固然这一切有赖于写 C++的人是否真的学会了这些,而一知半解的代价就是引起各种生命周期问题。
    但平心而论,rust 并没有真正去简化任何问题,它需要每个人在语法上付出额外的代价来获得一份来自编译器的保护,使得那些一知半解的人即使继续写出有问题的代码,至少不必付出实际代价(无法上线)。
    那么在座诸位,你是想继续做一个对生命周期问题一知半解,用丑陋的代码来换取编译器保护的人;还是想做一个彻底攻克生命周期问题,持续写出优雅的代码,并有机会探索更多语法可能性的人呢?
    ipwx
        107
    ipwx  
       2021-07-16 11:39:15 +08:00
    @wutiantong 有些人在编程语言上特别执拗,有种宗教狂热的感觉。比如王垠,不是瞧不起非 FP 语言么。安啦安啦
    levelworm
        108
    levelworm  
       2021-07-16 11:40:17 +08:00
    @ipwx 可能是我功力比较弱,所以往往会纠结,这个东西到底是不是一个人拥有呢?还是说弄不好写到后面发现要分享了?我总觉得得想好所有的架构才能开始写,所以特别痛苦。裸指针就爽多了,一把梭,反正是自己写的东西,泄漏就泄漏了。
    ipwx
        109
    ipwx  
       2021-07-16 11:41:36 +08:00   ❤️ 1
    @levelworm

    1. 想好所有架构开始写,这才是对的。不然你项目规模根本不可能突破一万行。
    2. 裸指针一把梭?泄露就泄露?朋友,习惯了这种写法,你项目永远做不大啊。
    ipwx
        110
    ipwx  
       2021-07-16 11:42:39 +08:00
    @levelworm 不过裸指针一把梭的情况也是有的,那叫 ObjectPool 对象池。一般用在算法内部分配数据结构节点的时候。先分配一大块内存,用就切,退出函数把大块内存给一起释放。
    levelworm
        111
    levelworm  
       2021-07-16 11:46:32 +08:00
    @ipwx 我的项目倒是都不大,比如说自己写的小游戏啊编辑器啥的。不过我其实是想想好再写的,我觉得代码写好是一个工程师的素质体现,哪怕是自己写的玩的。问题是我纯粹是业余写这个,所以写着写着就觉得大脑不够用了。这是我比较苦恼的地方。

    还有一个问题是和 C 库结合的问题,比如说 SDL2 。
    ppphp
        112
    ppphp  
       2021-07-16 12:36:26 +08:00
    c++语法支持了太多简单,一开始很好理解的小东西,导致本来很简单的小东西合一起用就很阴间很难懂很容易出错,而这种特性太多的问题是没有办法修复的,高级的概念并不能让人很好理解的话,很难淘汰低级的概念
    ipwx
        113
    ipwx  
       2021-07-16 12:50:20 +08:00 via iPhone   ❤️ 1
    @levelworm 用类包一下就好用了
    junkun
        114
    junkun  
       2021-07-16 13:32:09 +08:00   ❤️ 1
    @wutiantong C++也是有问题的,比如一个对象之前已经被 std::move 了,但 C++不会阻止你去访问一个 move 掉的对象,之后再调用这个对象的行为是一个 UB,相当于一个悬挂指针。
    rust 显式定义生命周期的目的,不是为了计算对象什么时候 destroy (这一点 rust 也是 RAII ),而是为了保证你在函数内访问或返回一个对象的引用的时候,这个引用一定是 valid 的。
    wutiantong
        115
    wutiantong  
       2021-07-16 13:52:50 +08:00
    @junkun

    1,访问一个移动后的对象不是 UB,请参考: https://en.cppreference.com/w/cpp/language/move_assignment

    2,确实不应该继续使用一个移动后的对象,既然选择了 move 它,就隐含着“不会继续用它了”这样的判断

    3,大部分时候移动语义自动发生,较少时候需要手动 std::move(),请参考: https://en.cppreference.com/w/cpp/language/value_category

    4,“为了保证你在函数内访问或返回一个对象的引用的时候,这个引用一定是 valid 的”,所以 rust 到处都是对象的引用吗?难怪会变成这样呢。如今用 C++可以大量地运用值语义类型,结合 const & 你都很难看到 new / 指针 / 左值引用,别提多爽了。
    no1xsyzy
        116
    no1xsyzy  
       2021-07-16 13:59:23 +08:00
    @wutiantong rust 的复杂生命周期语法在大部分代码中是不需要写的。
    而且其实 ponylang 更好,有 GC 但不会 stop the world,因为 GC 只会发生在 pony 代码不运行的时候,反而是如果没写好更可能泄漏(
    wutiantong
        117
    wutiantong  
       2021-07-16 14:09:23 +08:00
    @no1xsyzy ponylang 是新语言吗?还没怎么听过呢。

    我完全理解 “在大部分代码中不需要写”,就好像有些人用智能指针时连 weak 都没弄明白。

    毕竟这里很多人连写库与写应用的差异都没有体会。
    ipwx
        118
    ipwx  
       2021-07-16 16:11:39 +08:00
    @junkun @no1xsyzy 弱弱的说一句,有个现代 IDE 把关(比如 CLion ),不忽略 IDE 的检查,基本上 UB 对象调用在刚写出来就会标出来。。。 所以只要程序员脑子不出问题,这方面就真不是问题。

    如果你不相信你的程序员的脑子,那确实 C++ 不适合。
    junkun
        119
    junkun  
       2021-07-16 17:36:34 +08:00
    @ipwx 我是没看过哪个 c++的编译器能检查悬浮指针的问题的。微软和谷歌这些大量程序员用 c++的公司,都承认改用 rust 后,解决了绝大部分用 c++出现的内存错误。也许是他们的程序员脑子也不行吧。
    cheng6563
        120
    cheng6563  
       2021-07-16 17:43:56 +08:00
    感觉万恶之源就是兼容 c
    junkun
        121
    junkun  
       2021-07-16 18:03:42 +08:00   ❤️ 1
    @wutiantong 也许说 UB 确实不准确,但是问题就在于 c++并不阻止你使用再次使用 moved 的对象,即使它有潜在的问题。就像 c++不阻止你再次使用 deleted 的指针。虽然某些次运行不会出错,但是总有出错的时候。
    而且,c++就算用值语义,也不能避免内存错误,但是 rust 能检查出来。就比如有 std::vector<Foo> a={...}; const Foo &b = a[0]; a.clear();这时候就有潜在的悬浮引用 b 。
    3dwelcome
        122
    3dwelcome  
       2021-07-16 18:12:08 +08:00
    @junkun "就像 c++不阻止你再次使用 deleted 的指针。"

    所以养成好习惯很重要,微软都有推荐用 SAFE_DELETE 置空 deleted 对象指针。也有推荐 reset 掉 std::move 后的野内存。

    泄漏检测工具也很多,都 2021 年了,内存应该不再是 C++主要的问题了。
    junkun
        123
    junkun  
       2021-07-16 18:20:30 +08:00
    @3dwelcome 并不是,如果这些泄露检测工具那么有用的话,windows 和 chrome 也不至于总能找到内存问题吧。微软和谷歌的报告也指出了,他们产品大部分的漏洞都是内存安全导致的。而换用 rust 后,也都有称赞 rust 确实解决了大部分内存安全的问题。
    junkun
        124
    junkun  
       2021-07-16 18:32:23 +08:00
    @3dwelcome 就比如搜索 chrome 内存安全,就可以看到一篇报道,指出:自 2015 年来,use-after-free 占 chrome 安全漏洞的 36.1%。内存问题仍然是 c++的主要问题之一。
    lesismal
        125
    lesismal  
       2021-07-16 18:43:53 +08:00
    @byte10 求“高手”不要黑我golang
    wutiantong
        126
    wutiantong  
       2021-07-16 18:46:04 +08:00
    @junkun
    你说得其实大体都对,而且我觉得也没必要说服你去喜欢 C++。
    事实上,确实不断地听说一些基础项目在用 rust 重写,
    所取得的喜人效果也实属意料之中,令人欣慰,非常支持。
    也许这其中也有你的贡献,谢谢。
    no1xsyzy
        127
    no1xsyzy  
       2021-07-16 18:58:53 +08:00
    @wutiantong 新,但非很新。主要是运行模型比较不寻常,而且目前语言的不稳定度比 rust 还高,所以多数人没有听说过,也不会听说。特性是 GC 、没有竞态条件、没有锁、Actor 异步模型、严格的变量可用性。

    以及语言特性保证你像其他语言 for 就会泄漏(
    因为没有竞态条件,所以 GC 只会在你不运行的时候运行,所以 for 循环中会产生大量的对象而不作任何 GC 。你正常地打印一亿个数就需要 timer (

    @ipwx 主要还是 C++ 积重比较多,抛弃这些遗留问题之后跟 Rust 没两样,我毫不怀疑这样的 C++ 能和 Rust 对译(大概需要借助一些兼容层)。
    lesismal
        128
    lesismal  
       2021-07-16 18:59:33 +08:00
    @ipwx
    你们几位老鸟说的都是 cpp 这样或者那样用没问题。

    但问题是:
    假设 cpp 诞生后的 c with class+stl 是婴儿,tr1 boost 是青少年,c++11 及以后算是成年。
    越来越少的人有精力坚持到成年,快速发展的行业里爆发增长的业务需求没有时间等 cpp 项目上线和缓慢的迭代,那样子可能版本还没发出来公司已经倒闭了。
    并且通常来讲,c with class stl 已足够做项目,我就是保持停留在这个阶段,否则就可以直接宣布 c 去死了。

    所以你们说的不是问题中的问题跟其他人说的问题根本不是在聊同一个问题。

    “如果不用 xx 代码量会 5w”之类的,也不是什么问题,代码量多了一点,但是直观、可读性的提升,可以让更多使用婴儿 cpp 的人接手和维护,否则你看吧,招个人都费劲,说不定再过二十年,招懂 cpp 11-39 的程序员,就类似美国那个什么需要招 cobol 古董程序员求而不得的情况了。

    老项目、性能敏感领域、团队技术栈等因素考虑,cpp 确实还有很多市场。但对于新项目,即使性能敏感,如果团队能力 ok,rust 确实是更好的选择。性能不极度敏感的,go 是更好的选择。
    lesismal
        129
    lesismal  
       2021-07-16 19:02:27 +08:00
    @no1xsyzy
    “特性是 GC 、没有竞态条件、没有锁、Actor 异步模型、严格的变量可用性”
    —— 如果保证这些,那实现这些的每一点都是以牺牲性能为代价的,甚至我怀疑它会降级为脚本、类似 py GIL 伪多核
    lesismal
        130
    lesismal  
       2021-07-16 19:03:12 +08:00
    lesismal
        131
    lesismal  
       2021-07-16 19:04:18 +08:00
    @lesismal “tr1 boost 是青少年”指 boost 早期的时代
    Austaras
        132
    Austaras  
       2021-07-16 19:26:16 +08:00
    @wutiantong 那 cpp 支持的可以简单地用 raii 解决的问题 rust 里也没让你标记啊。。。一般来说需要手动标记的情况 cpp 都是解决不了的比如各种引用飞来飞去。此外所谓的“语法可能性”是什么奇怪的概念,不要再随地发明了

    此外 rust 的泛型和 adt 配合 pattern match 是比 cpp 现有的解决方案优雅地多的做法,可能你这时候又要说这只不过是语法不解决真正问题了
    Austaras
        133
    Austaras  
       2021-07-16 19:28:56 +08:00
    另外要说丑陋,不知道 cpp 到底是怎么有资格鄙视其他语言的。。。
    lesismal
        134
    lesismal  
       2021-07-16 19:48:49 +08:00
    除了那些少量设计有量、层次和模块相对稳定,并且开发人员严格控制的项目,比如内核
    而多数项目中,面向对象、设计模式都解决不了长期迭代后代码变屎山的问题,周期性重构才行

    cpp 是在把屎山堆得更大,而 rust 相当于对它的重构
    XIVN1987
        135
    XIVN1987  
       2021-07-16 21:14:28 +08:00
    我也觉得 C++应该经历一次 Python2 到 3 那样的不兼容升级,丢掉一些历史包袱,,不然的话真的是越来越复杂、难以掌握了。。
    levelworm
        136
    levelworm  
       2021-07-16 22:05:30 +08:00
    @XIVN1987 这个是越早越好,我觉得已经过了,现在只能朝大而全的路线走。。。
    levelworm
        137
    levelworm  
       2021-07-16 22:08:28 +08:00
    @ipwx 多谢。咳我觉得我就是太挫了。。。
    todayisgood
        138
    todayisgood  
       2021-07-16 23:32:38 +08:00
    讨论的真的不想写 C++了, 语言解决问题的工具而已,都是写业务代码,何必搞得自己这么累。
    我写 java,go 都简单
    Kingfree
        139
    Kingfree  
       2021-07-16 23:54:24 +08:00
    Rust 解决了 C++ 大部分痛点,目前已经在公司推了
    3dwelcome
        140
    3dwelcome  
       2021-07-17 00:06:32 +08:00
    @junkun “就比如搜索 chrome 内存安全,就可以看到一篇报道,指出:自 2015 年来,use-after-free 占 chrome 安全漏洞的 36.1%。”

    我觉得吧,BUG 还是和项目的体量正相关,并不是说用别的语言 BUG 少,而是缺少 chrome 这种超级巨无霸项目。

    在管理超级大项目的时候,C++其实还挺占优势的,因为语言出现的足够早,有各种架构研究。

    人经验上去了,遇到的坑多了,BUG 自然就少了。
    junkun
        141
    junkun  
       2021-07-17 00:21:46 +08:00
    @3dwelcome 我主要想说的是,内存安全漏洞占所有漏洞的比例一直是很高的,这反应的是 c++现有的范式及辅助工具等,并不能防止这些错误,而不是说 c++漏洞多。所有语言都能写出有漏洞的代码,但是有的语言就能保证(在 safe 的范围内)不犯某些错误。比如有 gc 的语言,你就不会内存泄露,而 rust 编译器保证了内存安全和并发安全( unsafe 以外)。
    “人经验上去了,遇到的坑多了,BUG 自然就少了。”才是无稽之谈,世界上就没有不犯错误的人,更不乏反复犯同一个错误的人。
    3dwelcome
        142
    3dwelcome  
       2021-07-17 00:35:39 +08:00
    @junkun "世界上就没有不犯错误的人,更不乏反复犯同一个错误的人。"

    不不,这锅不能让 C++语言来背,并不公平。

    我个人觉得,无关语言,而是写代码那个“人”,才是产生 BUG 最重要的主体。并不是语言之间有高下。

    可能你在以往项目里,遇到了很讨厌的 BUG,非常难查,所以顺带着厌恶 C++。但是也有很多老项目,运行的非常良好,代码结构清晰。就看开发的人,有没有心把代码写好,易维护了。
    3dwelcome
        143
    3dwelcome  
       2021-07-17 01:03:58 +08:00
    C++目前作为万能胶水语言,在各个领域都占有一席之地,如果是多平台高性能开发,就很难完全避免 C++。

    正因为 C++被定位”高性能“,和机器运行机制最接近,那些影响性能的动态类型,都被尽可能的排除在外(游戏行业为了追求帧率,还把 RTTI 都关掉),结果就是无比复杂的编译期推导,这也是过于追求性能的代价。

    内存泄漏也是一样,GC 很早就提出 C++ std 预案了,一直悬而不决,肯定不是做不了,而是加了必然会影响性能。

    游戏里有一种叫帧内对象的内存池。有些内存分配的对象,真不用去管什么内存泄漏,无脑用就可以。三帧后,整个内存池都不在了,必然会被回收。
    3dwelcome
        144
    3dwelcome  
       2021-07-17 01:08:21 +08:00
    关于 UB 我也想说一句,你遇到的坑,基本前人都遇到过。

    我最近遇到的一次,就是 memcpy 结果不稳定,网上一查,写明了 If the objects overlap, the behavior is undefined.

    很多时候真是吃一堑长一智。
    irytu
        145
    irytu  
       2021-07-17 02:21:13 +08:00 via iPhone
    Rust 来了!
    piping
        146
    piping  
       2021-07-17 08:26:06 +08:00   ❤️ 1
    c++ 设计就是工具的问题,跟人无关,这个工具门槛高,不好用,但以前没有可行的替代品,现在有了,就是 rust,这不是未来,是已经发生的事,以后只会越来越明显。
    piping
        147
    piping  
       2021-07-17 08:31:30 +08:00
    未来写 rust 代码的门槛只会越来越低,现在很多领域都有成熟的 rust 代码库,就算自己造论子,大部分时候 .clone() 完全足够使用,完全不需要学习 lieftime,rust 2021 版本马上要出了,现在的 rust 写起来就和脚本一样,速度还特别快
    junkun
        148
    junkun  
       2021-07-17 16:38:43 +08:00
    @3dwelcome 你的意思是,没有不好用的语言,只有写 bug 的人呗。但是前半句话就不成立,不然同样是底层高性能,为什么不用 C ?
    C++的问题不是有 UB,而是你用正常的代码写着写着,不知道什么时候就 UB 了,这才是 C++恶心的地方。而其他语言易用之处也就在于此,就是正常的代码的行为都是“符合预期”的,要用指针等危险代码就要用特殊语句。
    就比如你说的那个 memcpy overlap 的问题,rust 里,专门把 memcpy 拆成了 std::ptr::copy 和 std::ptr::copy_nonoverlapping 。就相当于跟你说清楚,有可能 overlap 的情况用这种,你能保证不可能 overlap 的情况用这种。
    abcbuzhiming
        149
    abcbuzhiming  
       2021-07-18 16:33:57 +08:00
    @Kingfree rust 在解决 c++痛点的同时又加了一堆自己的痛点上去了。。。
    newmlp
        150
    newmlp  
       2021-07-18 21:50:13 +08:00
    @junkun 内存安全不等于内存泄漏
    junkun
        151
    junkun  
       2021-07-19 03:04:55 +08:00
    @newmlp 注意看,我说的是有 gc 的语言不会内存泄露,而没有说 rust 不会内存泄露。
    mingl0280
        152
    mingl0280  
       2021-07-19 04:12:17 +08:00 via Android
    @junkun 说白了就是你对 C++的 UB 不熟悉然后又要怪 C++难写呗。
    就是又要骚操作又要装成专家,你这么搞怎么可能不出事?
    wutiantong
        153
    wutiantong  
       2021-07-19 10:58:44 +08:00
    @Austaras “语法可能性”就是指 c++的语法 feature 多呀,你真的可以多了解了解。
    比如你特意提到的 adt,我去搜了一下,好像就是 std::variant ? c++的一个小工具类原来在你心目中有那么厉害?
    wutiantong
        154
    wutiantong  
       2021-07-19 11:01:27 +08:00
    @Austaras 另外,你说的引用飞来飞去确实会令人头大,所以我一直在告诉你,一个可能是你的代码结构有问题,二个是如果你用 C++善用值类型封装,你就不会看到引用飞来飞去了。
    wutiantong
        155
    wutiantong  
       2021-07-19 11:07:38 +08:00
    @lesismal 学 C++可能是比较难的,但也没有你们说得夸张(很多事情真的会被无限的夸张渲染),不用管上古语法和 boost,直接从 C++11 学起,除了 C++ Primer 还推荐看这些:
    1. https://www.stroustrup.com/tour2.html
    2. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
    wutiantong
        156
    wutiantong  
       2021-07-19 11:08:59 +08:00
    @no1xsyzy 很有趣的样子,谢谢推荐
    mmdsun
        157
    mmdsun  
       2021-07-19 12:44:34 +08:00 via Android
    C++的升级版 C++++ = 微软的 C#
    bluesenzhu
        158
    bluesenzhu  
       2021-07-19 13:01:19 +08:00 via Android
    @junkun c/c++也有,memcpy()和 memmove()
    junkun
        159
    junkun  
       2021-07-19 14:04:41 +08:00
    @mingl0280 请问敢说自己精通 c++?
    RobberPhex
        160
    RobberPhex  
       2021-07-19 14:12:35 +08:00
    我 off topic 一下哈。

    《人月神话》书中提了软件工程的复杂度主要来自两个方面:本质性的的困难、附属性的困难。

    题主所属的,C++过于复杂,可以从其他语言吸收特性等,都是附属性的困难。

    但需要注意的是,“没有银弹”的主要原因,是本质性困难的存在。本质性的困难难以解决,因为大部分的活动是发生在人们的脑海里,缺乏有效的辅助工具:
    * 复杂性( complexity ):软件要解决的问题,通常牵扯到计算步骤,这是一种人为、抽象化的智能活动,多半是复杂的。
    * 隐匿性( invisibility ):尚未完成的软件是看不见的,即使利用图标说明,也常无法充分呈现其结构,使得人们在沟通上面临极大的困难。
    * 配合性( conformity ):在大型软件环境中,各子系统的接口必须协同一致。由于时间和环境的演变,要维持这样的一致性通常十分困难。
    * 易变性( changeability ):软件所应用的环境常是由人群、法规、硬件设备、应用领域等,各因素所汇集而成,而这些因素皆会快速变化。

    所以编程语言的发展还是有必要的,在解决附属性困难上很有必要。
    但是如果我们要大幅提升开发效率,目前比较好的方向是 AI 、形式化验证等。现阶段,编程语言的进化在解决本质性困难上用处不大。
    junkun
        161
    junkun  
       2021-07-19 14:20:22 +08:00
    @RobberPhex 我觉得你这结论不对。开发中处理本质性的困难就已经很麻烦了,可是如果编程语言 /工具又增加了附属性的困难,那不是难上加难。这也是为什么有的语言可能开发效率高,有的语言可能开发效率低。
    junkun
        162
    junkun  
       2021-07-19 14:29:17 +08:00 via iPhone
    @RobberPhex 这也是我觉得 rust 优于 c++的原因,在写 c++的时候我需要花费大量精力去检查是否有 UB 等算是附属性困难的问题。而 rust 中,在 unsafe 以外我就完全不需要考虑这些,只要让编译器通过即可,甚至可以差不多像托管语言一样编程,称之为解放感也不足为过。
    Austaras
        163
    Austaras  
       2021-07-19 14:41:28 +08:00
    @wutiantong  首先 adt 起码对应 std::variant 和 std::tuple,你可以看看 cpp 里的实现是多扭曲,而且 cpp 里也并没有相应的基础设施,随便多嵌套几层就等着爆炸吧

    feature 并不是越多越好,恰恰相反,需要仔细斟酌加什么不加什么,衡量收益和代价,而在这点上 cpp 可以说是灾难性的失败
    Austaras
        164
    Austaras  
       2021-07-19 14:42:54 +08:00
    @wutiantong  你都封装成智能指针了那哪里的 zero cost 呢?固然 rust 这边可以全部 Arc (不多线程共享的可以用 Rc,这点上成本比 cpp 的 shared_pointer 还要低),但性能损失呢?
    wangxn
        165
    wangxn  
       2021-07-19 14:56:30 +08:00 via Android
    @Austaras 没理解,你不能拿两个不等同的东西来比吧。shared_ptr 只能用来和 Rc 比。C++假如有需要也可以写一个单线程专用的。而且我不觉得这么简单几行就能实现的东西 Rust 还能比 C++快。
    wangxn
        166
    wangxn  
       2021-07-19 14:57:59 +08:00 via Android
    @wangxn 只能和 Arc 比。按你的说法。
    Austaras
        167
    Austaras  
       2021-07-19 15:00:04 +08:00
    哦,std variant 果然也没有 niche 优化,这可真 zero cost
    Austaras
        168
    Austaras  
       2021-07-19 15:05:01 +08:00
    @wangxn 那你要怎么知道这个东西不会被多线程共享呢?不好意思 cpp 没有这样的能力,只好全部上 Arc
    wangxn
        169
    wangxn  
       2021-07-19 15:07:05 +08:00 via Android
    @Austaras 难道说 Rust 能编译时或运行时自动判断?
    wutiantong
        170
    wutiantong  
       2021-07-19 15:07:13 +08:00
    @Austaras
    std::variant std::tuple 我不仅看过,也在不断地大量使用。

    我其实不明白你们为何那么执着要证明 rust 比 C++更好,
    而且我也没看到任何有说服力的证据,翻来覆去说的都是一些老生常谈。

    毕竟在我看来,你们口中各种对 C++的控诉,看起来似乎只能证明你们并没有学会这门语言的正确写法。

    那么,根据你刚才的两条回复,我能否请你:

    1. 举出一个实际代码的例子,展示 rust 的 adt+pattern match 是如何漂亮地抽象了一个复杂问题,我可以试试用 C++是否会写得丑陋且爆炸。

    2. 举一个引用必须要飞来飞去地例子,来展示通过声明周期标记 rust 编译器是如何为我们节省思虑并且提高性能的,我可以试试用 C++是否必须要牺牲性能与可读性。
    Austaras
        171
    Austaras  
       2021-07-19 15:10:31 +08:00   ❤️ 1
    @wangxn 是可以,有两个 auto trait send 和 sync,快来学吧 233
    wangxn
        172
    wangxn  
       2021-07-19 15:12:03 +08:00 via Android
    @Austaras 我看了一下 Arc 的文档,假如我没理解错,用法和实现完全等同于 shared_ptr,没发现有什么特别的、可以超越 C++的地方。
    Austaras
        173
    Austaras  
       2021-07-19 15:17:08 +08:00
    @wangxn 超越的地方就是不需要线程共享的时候可以不用这个改用 Rc 啊,而且 rust 能保证 Rc 被共享的时候会编译出错
    paoqi2048
        174
    paoqi2048  
       2021-07-19 15:23:00 +08:00
    这个贴子现在已经变成了 Rust 厨和 C++厨的交锋了🙃
    Austaras
        175
    Austaras  
       2021-07-19 15:30:18 +08:00
    @wutiantong pattern match 的优越性是老生常谈了,各路 fp 语言都分别论证过一遍的东西,随手举几个例子
    ···
    fn foobar(n: u8) -> String {
    (match (n % 3, n% 5) {
    (0, 0) => "foobar",
    (0, _) => "foo",
    (_, 0) => "bar",
    (_, _) => ""
    }).into()
    }

    struct List<T> {
    value: T,
    next: Option<Box<T>>
    }

    fn len<T>(list: List<T>) -> usize {
    1 + match list {
    List { value: Some(list), ...} -> len(list) ,
    List { value: None, ...} -> 0
    }
    }
    ···
    复杂的例子里最好的例子大概是 ast,比如
    ···
    let persistent_id = match &mut item {
    // function Foo() {}
    ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { ident, .. }))) => {
    if let Some(hook) = handle_map.remove(&ident) {
    hook_reg.push((ident.clone(), hook));
    }
    get_persistent_id(ident)
    }

    // export function Foo() {}
    ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
    decl: Decl::Fn(FnDecl { ident, .. }),
    ..
    })) => {
    if let Some(hook) = handle_map.remove(&ident) {
    hook_reg.push((ident.clone(), hook));
    }
    get_persistent_id(ident)
    }

    // export default function Foo() {}
    ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
    decl:
    DefaultDecl::Fn(FnExpr {
    // We don't currently handle anonymous default exports.
    ident: Some(ident),
    ..
    }),
    ..
    })) => {
    if let Some(hook) = handle_map.remove(&ident) {
    hook_reg.push((ident.clone(), hook));
    };
    get_persistent_id(ident)
    }

    // const Foo = () => {}
    // export const Foo = () => {}
    ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl)))
    | ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
    decl: Decl::Var(var_decl),
    ..
    })) => {
    ···
    至于生命周期的例子,不好意思,cpp 只要愿意永远可以怼裸指针,而且 rust 里需要手动标记的地方也是越来越少,所以很难搞出一个具体的例子,不过你可以看看 rust 里的 COW 是怎么做的
    Austaras
        176
    Austaras  
       2021-07-19 15:35:38 +08:00
    Austaras
        178
    Austaras  
       2021-07-19 15:59:45 +08:00
    mingl0280
        179
    mingl0280  
       2021-07-19 16:18:08 +08:00 via Android
    @junkun 我不是精通 C++,我只知道有特性我不确定的时候我会去查特性怎么用是不是 UB,你又不是精通又要装精通那肯定觉得 C++好难啊。本质上你提出来的所有问题都是你自己对自己的能力判断过高然后却又非要装成老大哥来教育别人的问题。
    xcstream
        180
    xcstream  
       2021-07-19 17:06:27 +08:00
    c++版本众多,没有统一的设计模式。。
    junkun
        181
    junkun  
       2021-07-19 17:09:10 +08:00
    @mingl0280 C++的问题是,当你以为你会这个特性的时候,实际上这个特性在某种情况下又会有别的行为,连编译器不同都可能会有不同的行为,连正常的、简单的语句几乎处处都可以有陷阱。就请问您有多少种特性是能确定的,保证能考虑到各种情况的?你是每写一句代码就要查怎么用吗?
    Observer42
        182
    Observer42  
       2021-07-19 17:11:01 +08:00
    ADT + Pattern matching 在 Rust 真的是应用非常广泛了。C++的 variant 当然也算 ADT,但没有 pattern matching 用起来实在是比较痛苦。我理解 C++不搞 pattern matching,提供个 variant 完事,但硬说 C++的小工具类在 Rust 里被吹成宝,lol
    wutiantong
        183
    wutiantong  
       2021-07-19 17:25:04 +08:00
    @Observer42 我是说得夸张了些,见笑~
    junkun
        184
    junkun  
       2021-07-19 17:49:20 +08:00   ❤️ 1
    @wutiantong 我想到了一个生命周期的例子,但可能不太贴切。就比如有这样一个多层嵌套的结构:
    std::vector<std::vector<...std::vector<int>...>> mat;
    访问这个 vector 的某个元素就需要, mat[0][0]...[0]。为了可读性或者其他目的,C++里可能会设置引用,int &first_element = mat[0][0]...[0]。或者 std::vector<int> &sub_mat = mat[0]...[n]
    但 C++中,你仍然能对这个 vector 的某一层进行操作,比如 push_back,但是当 vector 的容量达到一定程度时,vector 需要扩容时,就可能导致引用失效。因此对 vector 操作时,C++程序员必须思考和检查有无指向 vector 内部的引用和指针。
    但在 rust 里,对 Vec 内部的引用,因为 get 函数返回的引用的生命周期依赖 Vec,因此会同时使 Vec 在内部引用周期结束前都保持只读状态,因此如果在引用周期结束前执行了 push 等操作,编译器就会直接报错。因此在 rust 中,就免去了人工思考能否 push 的过程。
    NCE
        185
    NCE  
       2021-07-19 19:06:49 +08:00
    @lesismal 假设 cpp 诞生后的 c with class+stl 是婴儿,tr1 boost 是青少年,c++11 及以后算是成年。 这句话说的好,但 cpp 的青少年和青少年,并不是像 C#,java 一样是主动成长的,cpp 更多是看着别的孩子长的那么快,自己被迫发育的,😂😂😂
    mainjzb
        186
    mainjzb  
       2021-07-20 10:26:53 +08:00   ❤️ 1
    其实他们说的 C++ 的 UB 不仅仅是 UB 还有其他一些莫名其妙你会犯的错误。
    例如一个简单的例子。


    if ( strcmp(stra, strb) ) {
    // do right something
    }
    else {
    //do false something
    }

    这段代码是错的。你一不小心就犯错了。而且 IED 没有任何提示。用 C++ debug 的错误的时间总比别的现代语言更长。
    也许有人会反驳这是对函数理解不到位,没看函数声明导致的。但我觉得的,这完全就像规定你每次走路必先出右脚一样,时时刻刻让你记住这个规则。一旦记错就会导致 bug 。这完全不合理。
    hitmanx
        187
    hitmanx  
       2021-07-20 10:53:40 +08:00
    @junkun 有点意思。

    有个问题,

    “因此如果在引用周期结束前执行了 push 等操作,编译器就会直接报错”,

    如果是跨函数甚至是跨 compilation unit 执行 push 等操作,编译器也能报错?比如,在取了 int&之后调用了一个函数,传递了 vector 的引用,然后在新的函数里面进行 push 操作。我总感觉这是 runtime 才可能捕捉到的错误,不太清楚在编译时如何预料到各种情况的。
    johnli
        188
    johnli  
       2021-07-20 11:24:47 +08:00
    @hitmanx 不允许两个&mut 生命周期交叉
    piping
        189
    piping  
       2021-07-20 12:39:48 +08:00   ❤️ 1
    比如 c++ 的 map, 标准库里的东西,可以这样写 `std::map<int,int> m; return m[0];`
    也许有人可以指出为什么这样的用法是错误的,但是编译器不会报错。
    标准库里这样最基本的数据结构的 API 设计都有问题,还能说 c++是一个适合新人学习,有开发效率的语言吗?
    wangxn
        190
    wangxn  
       2021-07-20 14:12:41 +08:00
    @mainjzb 严格来说 strcmp 是 C 的东西。C++ 的字符串比较直接用 ==。
    roudancongji
        191
    roudancongji  
       2021-07-20 14:25:45 +08:00
    c 艹历史包袱太重了,没法那么跳脱的
    cdxjcl123
        192
    cdxjcl123  
       2021-07-20 17:30:59 +08:00
    我选择 Rust
    lesismal
        193
    lesismal  
       2021-07-20 21:31:16 +08:00
    @wutiantong
    我个人也没有觉得 C++难,只是收益率太低了,书也无需推荐给我,我家里 C++的书恐怕 30 本不止,C++ Primer 这本书被严重夸大了,名不副实,稍微有用点的 effective/more effective,最喜欢的是对象模型,因为我写 C,不喜欢 C++那些隐藏的手段想弄清楚究竟。efficient/more efficient 、模板、侯捷 stl 源码、boost 各种系列,还有很多其他的记不起来了,太久远了都十几年前买来部分阅读的的书了,当然还有 BJ 老爷子那几本

    C++的主要问题是收益率:
    1. C++十年磨一剑 vs Java 三年架构师,这两个人群整体的工资、收入对比,这是对于个人职业发展的角度的考量。
    2. C++项目研发周期、项目维护、团队维持的难度、日后新业务迭代的速度 vs 其他语言,除了那些本身就需要性能为主的偏基础设施或老项目或其他一些既定领域已有解决方案,很多商业项目等不起 C++的节奏,等到 C++搞定天下,工资早倒闭了

    不管是个人职业规划还是商业项目,都讲究收益率,而且也并不是所有人都觉得 C++不难,反而,大多数人会觉得难,也不是难在“难于理解”,难在需要花大量时间,多数人没那么多时间或者舍不得花那么多时间。

    以上说的 C++问题,这种类似的讨论,以前在老论坛行也有接近 C++语言律师级别的大神经常讨论,至少是十年前、还没有 C++11 的时候大家就基本共识了

    再提醒下为什么你们几位一再挺 C++,你们没有错,但是你们可能不自知(比如我之前提到的,你们解释的问题根本不是其他人在说的问题):
    经济学有个词叫“沉没成本效应”,百度抄一段:“指为了避免损失带来的负面情绪而沉溺于过去的付出中,选择了非理性的行为方式。根据经济逻辑的法则,沉没成本与制定决策应是不相关的”,解释一下就是,如果你花费了大量的成本在某件事情上,舍不得放下或者承认他的不好。
    你们在投入了大量的学习成本之后,不自知地要维护 C++,否则就是否定自己的过去,而且目前仍旧可以用 C++做着喜欢的项目、获得不错的收入,但是,如果你曾经用于 C++上的钻研如果是换做其他领域,收益率可能远高于当下(当然并不是针对所有人,你可能是最优秀的那少数、收益率仍然非常高,我这里的意思是针对群体整体)。

    当未来某天你们跳脱出 C++的范畴,发现更多新大陆的时候,蓦然回首,再去比较,才会发现原来这世间有那么多比茴字的六种写法更有趣的玩物
    lesismal
        194
    lesismal  
       2021-07-20 21:48:48 +08:00
    补充,effective/more effective 以及类似的书,虽然是一些工程上的总结、给 cpper 带来了很多必坑指引,但也正是这些书,更加让孔乙己们深陷于茴字的 N 种写法无法自拔。虽然即使没有这些书,孔乙己类的人仍然会成为孔乙己,但是如果有其他的朴素工程哲学的书籍先入为主,或许能挽救很多 cpper 于水火,或许能让 C++不至于走上如今的道路。毕竟 c with class and stl,或者再来点 template,就已经足够做事了。
    lesismal
        195
    lesismal  
       2021-07-20 21:49:19 +08:00
    @lesismal #194 必坑指引 -> 避坑
    levelworm
        196
    levelworm  
       2021-07-21 01:21:10 +08:00   ❤️ 2
    @lesismal 我觉得 C++用的话得会做减法,尽量少用 feature,不然搞到最后真的是百花齐放。。。就我个人的学习项目,基本上 C with class 加上智能指针,和一点 template 就差不多了,再来点 auto 语法糖,就行了。
    zxCoder
        197
    zxCoder  
    OP
       2021-07-21 10:00:12 +08:00
    @levelworm 弱弱的问一句 像你做的这种个人项目,一般是什么相关的,因为像你说的“基本上 C with class 加上智能指针,和一点 template 就差不多了,再来点 auto 语法糖”,感觉其他语言也能写,而且更方便,硬着头皮写 c++总感觉体会不到乐趣
    bluesenzhu
        198
    bluesenzhu  
       2021-07-21 10:57:17 +08:00
    这几位 rust 粉丝还真是宗教狂热啊。
    rust 吹这么多年,做出什么杀手级项目来了?再给你十年时间还照样是小众边缘语言,除了自己玩玩,到哪里找工作去?
    c/c++作为工业标准,在业界就像水和空气,随便举例,比如 opencv,搞机器视觉的,谁绕得开?
    其实 C++的批评者也是矛盾的,一方面嘲笑 c++委员会动作慢,一方面又批评 c++1x,c++2a 新特性太多太复杂。
    运行效率和安全性在某种程度上是对立的,你们都忘记了 c++在某种程度上继承了 c 语言的哲学:相信程序员。
    rust 和 c++两个不一样的设计哲学,rust 非要踩 c++上位?踩得下、上得了吗?
    levelworm
        199
    levelworm  
       2021-07-21 12:15:07 +08:00 via Android
    @zxCoder 呃其实要么是简单的解释器要么是小游戏,的确完全没必要用 C++。问题是我这个最熟啊,所以就用着了。
    ihciah
        200
    ihciah  
       2021-07-21 13:28:16 +08:00
    不光要 easy to use,还要 hard to misuse 才好
    1  2  3  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1426 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 17:29 · PVG 01:29 · LAX 09:29 · JFK 12:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.