最近发现的 C++ 笑话: switch(a){ case 1: return 1;} - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GeruzoniAnsasu
V2EX    C++

最近发现的 C++ 笑话: switch(a){ case 1: return 1;}

  •  
  •   GeruzoniAnsasu
    pnck 2024-04-23 14:08:36 +08:00 4248 次点击
    这是一个创建于 567 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想必大家都听说过「 Java 程序员的吐槽」说低水平的同事写出这种代码:

    if(status == A) { playwith(A); } if(status == B) { playwith(B); } //... 

    然后这种吐槽的下面还会有不少人煞有介事地分析,说可能是老代码删改留下的痕迹,以前没有一个统一的 playwith() 函数云云……

    今天很高兴地通知大家,我们 c++,即使是 新代码,也完全可能 会写出这种段落,而且这种段落完全 有意义! :)

    Make sense 的关键在于, playwith()函数 不再一定能 在当前的函数上下文调用了。 c++20 引入的 consteval function 会阻止传入任何非 constexpr 的参数,所以你在一个运行时的 switch context 里是不能用运行期值去调用 consteval 函数的:

     // this lambda is conteval auto flatten = [](E _e) consteval { auto [id, extra] = static_map(_e); auto [name, info] = std::move(extra); return make_tuple(static_cast<int>(_e), id, name, info); }; 
     cout << std::apply( [](auto &&...args) { return std::format("in case {}, id {} (name:{}) has info: {}\n", args...); }, // flatten(FOO)); // work, but only with literal to_runtime(flatten, e)); // work at runtime 

    那这个 to_runtime() 怎么写呢? 没错,只能判断每个可能的编译期值了,于是我们得到了

    switch(c){case 1: consteval_fn(1);} 

    在 compiler explorer 上查看这个例子
    例子 on gist

    p.s. consteval 函数编译起来是真的很慢,为了这样一个 编译期 map 代价还挺大的

    15 条回复    2024-05-17 19:09:58 +08:00
    FlashEcho
        1
    FlashEcho  
       2024-04-23 14:14:38 +08:00
    没太理解这么做的意思

    consteval 函数最好是比较简单的单纯计算,所以我会声明它为 consteval 希望能在编译器搞完

    如果需要传入任何非 constexpr 的参数了,说明这个函数需要依赖复杂的上下文,为什么还要用 consteval ?在编译期又不一定能提速,开发麻烦,速度还不一定快
    GeruzoniAnsasu
        2
    GeruzoniAnsasu  
    OP
       2024-04-23 14:19:00 +08:00
    @chesha1 consteval 函数可以方便实现复杂的编译期 map ,但要在运行时查取这个 map 就会回到 switch 运行时值 case 编译期值的情况,然后 case CONST f(CONST) 就「看起来比较蠢但这里是 c++」
    nicaiwss
        3
    nicaiwss  
       2024-04-23 14:21:54 +08:00 via iPhone
    @GeruzoniAnsasu 编译期 map 有啥用?能举个例子吗?什么场景会用到?
    InkStone
        4
    InkStone  
       2024-04-23 15:02:56 +08:00
    这个点很有意思,不过从实用性角度考虑的话感觉不如写个脚本去生成硬编码的代码。
    MoYi123
        5
    MoYi123  
       2024-04-23 16:08:41 +08:00
    要在编译期算运行期的值只能打表啊, 这也没办法.
    lxdlam
        6
    lxdlam  
       2024-04-23 17:39:54 +08:00   2
    本质的核心问题是 C++ 没有真正的 exhaustive 的 enum ,导致编译器无法限定 `E` 的值在一个可优化的 immedate value 范围内,无法将其在 comptime 求出来,所以将其放到参数里无法做编译期生成。

    为什么 `to_runtime` 又能 work 了?因为在当前场景下,编译器推导出来对于 `consteval` 函数 `flattern`,可行的值是有限的,所以能够正常编译。把最后一个分支改为 `f(_e)`,编译器会报错 `f` 为一个 `consteval function`,但是参数是 comptime unknown 的。

    对于有 exhaustive enum 的语言( rust 、zig 等),要实现这种需求应该是 trivial 的。
    lxdlam
        7
    lxdlam  
       2024-04-23 17:43:08 +08:00
    @lxdlam 补一句,通过 template + if constexpr + static_assert 可能可以将这个 `to_runtime` 自动化生成出来,不过我也好久没写过 C++ 了,如果不行当我没说。
    cybort
        8
    cybort  
       2024-04-23 18:07:38 +08:00 via Android
    这相当于编译器给你打表,看着是 consteval_fn(1),其实这里是一个常数,但是写常数看不出是怎么来的。
    lostsummer
        9
    lostsummer  
       2024-04-23 18:07:50 +08:00
    实在忍不了。GoLang 中类型断言就是觉得有点丑陋但不得不用的东西,到 C++中能麻烦 3 倍!
    GeruzoniAnsasu
        10
    GeruzoniAnsasu  
    OP
       2024-04-23 18:14:10 +08:00
    @lostsummer

    你好,有种东西叫 typescript

    而且 c++的类型体操其实是越发展越简单的,新标准一直在引入能简化旧 trick 的写法 除了我还未能一窥真容的 executors
    flax5a98aa2
        11
    flax5a98aa2  
       2024-04-23 22:09:43 +08:00
    为什么要把打表写成 consteval 函数,而不是 constexpr 函数啊 o_o ....?想要保证 consteval ,写个新的 consteval 函数,转发参数到写好的 constexpr 函数上就行了。
    GeruzoniAnsasu
        12
    GeruzoniAnsasu  
    OP
       2024-04-23 22:51:53 +08:00
    @flax5a98aa2 我(们)离最佳实践还很远,较真来说。

    写 c++代码与其它语言最大的区别是,经常要从一大堆实现方法中 filter 出一个标准,而不是突破语言的限制尽可能地让程序灵活起来。

    所以 我不知道。 在确定 consteval 真的很有用之前我也宁愿普通地打表,更何况过去的那些代码生成器也不会给你生成新写法的代码。
    ljyst
        13
    ljyst  
       2024-04-24 00:41:30 +08:00
    这算什么。更底层的写单片机为了节省编译后的程序大小能从 500 字节压到 100+并且不是程序压缩而是手工去做,压缩前的版本单字节变量名或者写法这些自己和圈子老手一看就能看懂
    flyqie
        14
    flyqie  
       2024-04-24 15:17:04 +08:00 via Android
    请教下,大家生产环境会用这些看起来。。比较魔法的特性吗。

    感觉好像我自己看到的 cpp code 都不太会用这种奇奇怪怪的魔法,写法都很清真。。
    nooneanyone
        15
    nooneanyone  
       2024-05-17 19:09:58 +08:00
    @GeruzoniAnsasu #10 确实,这几天做老项目的代码迁移兼容,c++17+的代码改成 c++11 差点没把握整死了。后来还是打算用 17 编译不过不用高版本的 stl 库,这样来跑在旧环境。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5731 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 01:53 PVG 09:53 LAX 17:53 JFK 20:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86