【问题标题】:Usage of std::optional vs. "unused/default" valuesstd::optional 与“未使用/默认”值的使用
【发布时间】:2017-09-17 14:32:59
【问题描述】:

我正在编写一个代表按钮的类。这个按钮可能有也可能没有各种属性,比如上面写的文本、快捷方式、纹理或平面颜色填充。因此,例如,如果此按钮没有任何纹理集,则跳过纹理绘制过程。

我的第一个解决方案是使用虚构的默认值,表示给定属性未使用(如果颜色的 alpha 值为 0,则将跳过颜色填充绘图等)。

我的其他选择是使用新添加的 std::optional ,这样使用起来会更清晰、更简单。

这里是提到的两个例子:

class Button {
    void draw() {
        if (fill)
            drawRectangle(*fill);
        if (sprite)
            drawSprite(*sprite);
        if (font)
            drawText(name, *font);
    }

    std::optional<std::string> font;
    std::optional<std::string> sprite;
    std::optional<Color> fill;
}

class Button {
    void draw() {
        if (fill.alpha != 0)
            drawRectangle(fill);
        if (sprite != "")
            drawSprite(sprite);
        if (font != "")
            drawText(name, font);
    }

    std::string font;
    std::string sprite;
    Color fill;
}

在这种情况下,使用 std::optional 的优点和缺点是什么?我主要感兴趣的是内存使用和开销差异。

我也应该只是,而不是使用 if 来检查 optional 是否包含值,而是调用 value() 并捕获异常吗?

【问题讨论】:

  • 关于使用.value() 并捕获异常而不是检查它是否具有值的问题:这是个坏主意。顾名思义,异常是针对特殊情况的,堆栈展开造成的开销不可忽略。假设有很多按钮,例如不会有颜色,可选的为空根本不是例外情况。
  • 我不认为std::optional&lt;std::string&gt; 非常有用,除非空字符串与没有字符串完全不同。
  • std::optional 用于函数返回,而不是类成员。这是一件非常浪费的事情。另外,请注意 sprite.empty() 应该使用 sprite == ""
  • std::optional 有内存开销(可能类似于额外的bool
  • 我看不出std::optional 有什么优势。此外,请考虑主要的 UI 实现,例如 AWT 和 .NET:空字段实际上是空的,而不是“不存在”。同样,您不会从中获得价值。

标签: c++ optional c++17


【解决方案1】:

就开销而言,它主要是空间的形式。 optional 总是占用它存储的任何东西,加上一个布尔值,加上任何额外的填充以使对齐工作。例如,std::string 通常实现为 24 字节,8 字节对齐。 optional&lt;string&gt; 将是 25 个字节,但由于对齐,它最终将是 32 个字节。对于原始类型(int 或 enum),它通常会将所需的存储空间从 4 个字节增加到 8 个字节或类似的东西。

就性能而言,在这种情况下,缓存效果之外没有区别(如果优化器很聪明)。将std::string 与空字符串文字进行比较可能会优化为对std::string::empty 的调用(您可能应该这样写),这只是检查整数是否为零,这与您对@ 的比较相同987654327@,和判断一个布尔值是否为零基本一样。

也就是说我喜欢optional;我认为它更清楚地传达了代码的意图。但是,如果您有非常明显的哨兵价值,那么它的价值可能就不那么大了。

在某些情况下,您可以选择使用紧凑型蛋糕并吃掉它:https://github.com/akrzemi1/compact_optional。它具有与常规可选项相同的外部接口,但您给它一个哨兵值,它使用该哨兵来存储缺失的状态。但可能不适用于所有类。

【讨论】:

  • +1。关键要点是“更清楚地传达意图”和“如果你有非常明显的哨兵价值,那么它可能就不那么有价值了”清楚地传达意图基本上优先于除“易于阅读”和“难以误用”
【解决方案2】:

虽然 std::optional 可能会产生额外布尔值的开销,但它在这里也具有描述性目的:它充分表达了您试图放入代码中的概念,这使得该代码直接向读者展示了什么是继续。因为这是 UI,而且布尔开销相对较小,我会说去吧。

我对 std::optional 仅用于函数返回的说法提出异议。那将是非常有限的。

【讨论】:

    猜你喜欢
    • 2021-10-27
    • 2023-03-15
    • 2021-06-06
    • 2017-12-18
    • 2022-10-24
    • 2022-06-10
    • 2011-01-08
    • 2010-12-09
    • 1970-01-01
    相关资源
    最近更新 更多