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

请教大家一个函数的定义问题

  •  
  •   ligiggy · 2022-01-25 10:45:39 +08:00 · 2493 次点击
    这是一个创建于 1067 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题如下:

    • 需要定义一个函数,用来计算结果,结果为 double ,但是计算有可能出错,使用函数需要判断出错与否。

    这个时候你的函数是:

    double getCalculateResult(bool &ok);
    

    还是

    bool getCalculateResult(double &result);
    
    26 条回复    2022-01-25 23:50:51 +08:00
    socketpeng
        1
    socketpeng  
       2022-01-25 10:57:07 +08:00
    如果是我自己,会使用第二种。个人认为第二种写法更为直观,而且还可以把函数当成表达式放进 if 判断中
    justou
        2
    justou  
       2022-01-25 11:00:07 +08:00   ❤️ 10
    1. double getCalculateResult(bool &ok); 表示需要强制判断
    2. bool getCalculateResult(double &result); 完全可以忽略判断
    3. double getCalculateResult(bool *ok=nullptr); 是否判断由调用者决定
    4. std::optional<double> getCalculateResult(); 另一种强制判断
    5. 抛异常

    得看具体场景
    ychost
        3
    ychost  
       2022-01-25 11:02:41 +08:00   ❤️ 2
    我更喜欢抛异常
    ripperdev
        4
    ripperdev  
       2022-01-25 11:04:30 +08:00   ❤️ 2
    ```cpp
    std::pair<double, bool> getCalcResult();
    auto [ret, ok] = getCalcResult();
    ```
    newmlp
        5
    newmlp  
       2022-01-25 11:06:32 +08:00
    你爱咋定义咋定义呗,这又没标准答案
    ligiggy
        6
    ligiggy  
    OP
       2022-01-25 11:09:07 +08:00
    @newmlp 2 楼大哥 YYDS
    ligiggy
        7
    ligiggy  
    OP
       2022-01-25 11:09:38 +08:00
    @justou 感谢感谢,受教了。我没意识到第三种的好处,哈哈,就第三种了。
    2i2Re2PLMaDnghL
        8
    2i2Re2PLMaDnghL  
       2022-01-25 11:15:44 +08:00   ❤️ 1
    @justou 再来点迷惑操作
    (double*) getCalculateResult(); // 结果放堆上,用 NULL 表示出错,需要调用方 free 之
    ligiggy
        9
    ligiggy  
    OP
       2022-01-25 11:17:19 +08:00
    @2i2Re2PLMaDnghL 瞎搞要被 ban
    Building
        10
    Building  
       2022-01-25 11:17:43 +08:00 via iPhone
    这个时候你就知道元组有多好用了
    ligiggy
        11
    ligiggy  
    OP
       2022-01-25 11:32:01 +08:00
    @Building 我不认为需要元组这种东西
    jmc891205
        12
    jmc891205  
       2022-01-25 11:36:19 +08:00
    std::tuple<double, bool> getCalResult();
    Building
        13
    Building  
       2022-01-25 11:37:30 +08:00 via iPhone
    @ligiggy 假设解多元多次方程,有多个答案的时候,是不是就需要元组了?当然你可以用结构体,字典,数组等等,但是随时随地定义一个元组,是不是最直观最简单?
    ligiggy
        14
    ligiggy  
    OP
       2022-01-25 11:43:12 +08:00
    @Building 在我的印象中,元组的可读性很差,尤其是访问结果的时候
    dangyuluo
        15
    dangyuluo  
       2022-01-25 11:50:29 +08:00   ❤️ 2
    std::optional 就是干这个用的,更激进一点 std::expected
    dangyuluo
        16
    dangyuluo  
       2022-01-25 11:51:32 +08:00
    @ligiggy tuple 的可读性差但是性能很好,再说两个元素直接用 pair 好了

    另外,tuple 用好了(配合 meta programming )简直是大杀器
    MegrezZhu
        17
    MegrezZhu  
       2022-01-25 11:57:05 +08:00
    absl::StatusOr<double>
    ligiggy
        18
    ligiggy  
    OP
       2022-01-25 11:59:30 +08:00
    @dangyuluo 我秉承 程序是写给人看的观点,能不用 tuple 和 dictionary 之类的 key value 就会避免掉。
    mainjzb
        19
    mainjzb  
       2022-01-25 14:02:47 +08:00
    [[nodiscard]] bool getCalculateResult(double &result);
    C++远古写法

    std::optional<double> getCalculateResult();
    java 写法

    std::tupl<double, bool> getCalculateResult();
    go 写法
    yolee599
        20
    yolee599  
       2022-01-25 14:05:39 +08:00
    写 C 的,更喜欢第二种
    easing
        21
    easing  
       2022-01-25 14:12:13 +08:00   ❤️ 1
    std::optional 就是来解决这个问题的,自己实现一个也不是什么难事。
    xiao109
        22
    xiao109  
       2022-01-25 15:11:03 +08:00
    老生常谈了,抛异常 vs 返回值。
    ColorfulBoar
        23
    ColorfulBoar  
       2022-01-25 15:30:15 +08:00   ❤️ 2
    你这个问法可能不太对……首先不怎么建议问(不管别人还是自己)选 A 还是选 B 这种问题,因为答案很可能是 C ( std::optional 或者 std::expected ),也可能预设的选项会干扰别人的思路,而且很可能会得到一堆没怎么说清楚理由的个人偏好。

    一个可能的一般性思路是如果不是很确定怎么解决一个问题的话,那我们可以尝试找个能同时解决多个问题的方案(一方面能交叉对比排除掉一些错误答案,一方面一个模式如果是有效的话很难相信只在一个地方起作用)。比如它最好能同时锻炼某种学习 C++时需要的独立整蛊能力和提供能迁移到其他语言中的知识。所以或许可以尝试一下下面这套办法:
    1. 先找找最新的与 error handling 相关的 proposal ,然后发现最近的进展差不多在 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html 里面( std::expected ),花几分钟读完 motivation ,就脱离了很多人长时间里陷入的那种凭感觉选一种方式但也说不清为什么的阶段了。继续读需要先看看里面提到的 std::optional 那篇(以及后续的改进 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r8.html 与 std::expected 那篇里面提到的类似 proposal ),如果感觉读着很爽就直接顺着往下看。
    2. 由于 C++23 还不知道什么时候能用上,使用 std::expected 的例子可能不太多,纯读 proposal 如果感觉无聊 /读不下去的话可以拿对 std::optional 的讨论与使用凑合一下(比如 https://devblogs.microsoft.com/cppblog/stdoptional-how-when-and-why/,或者 CppCon 里面应该有一些相关的 talk ,由于一个话题很可能会讲好几次所以挑个喜欢的跳着看吧)然后假装 std::optional<T>就是 std::expected<T, nullopt_t>来辅助理解 std::expected 。另一个选择是看一下 Rust 里面和 std::expected<T,E>类似的处理方式 Result<T,E>是怎么设计和使用的( https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.htmlhttps://doc.rust-lang.org/rust-by-example/error.html
    3. 以上文档可能还有一些没直接说清楚的问题(比如 pair<bool, T>和 optional<T>不考虑后者在不必要时能不初始化 T 等问题之后好像有点像,或者除了感觉不爽以外还能用什么借口拒绝 tuple ,或者为啥那 proposal 要用 monadic 这么神经病的词,或者什么别的在你心里像是长了条猪尾巴一样乱晃的问题),这个时候看一些抽象一点却又没那么抽象的东西,比如 type system 相关的材料,会比较舒服。关键字大概是 algebraic data type, product/sum type 和 monad 。一个比较具体但不够深入的选择是 Programming with Types: Examples in TypeScript 的第 3 章和第 11 章,当然如果不喜欢 Typescript 或者嫌它没讲清楚(的确如此)也有大把的其他饲料可以选择就是了。
    ligiggy
        24
    ligiggy  
    OP
       2022-01-25 16:03:37 +08:00
    @xiao109 没有人 care 异常
    jujusama
        25
    jujusama  
       2022-01-25 17:18:37 +08:00
    std::optional 或 std::pair<double, bool>

    后者参考 std::map::insert | emplace
    whenov
        26
    whenov  
       2022-01-25 23:50:51 +08:00 via Android
    @ligiggy 使用 structured binding 的话,std::tuple 可读性并不差。当然我觉得这里用 std::optional 最好
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   967 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:03 · PVG 04:03 · LAX 12:03 · JFK 15:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.