Using std :: optional and unused / default values

I am writing a class that represents a button. This button may or may not have various attributes such as text written on it, label, texture, or flat color fill. So, if, for example, this button does not have any set of textures, the process of drawing the texture is skipped.

My first solution was to use the default values ​​I made, which say that the given attribute is not used (if the alpha value was 0, the color fill drawing will be omitted, etc.).

Another option I have is using the recently added std :: optional, which will be much clearer and easier to use.

Here are the two mentioned examples:

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;
}

      

What could be the advantages and disadvantages of using std :: optional? I am mainly interested in memory and overhead usage issues.

Also, do I just need to use, if to check if there is an optional value, call the value () and throw an exception?

+3


source to share


2 answers


When it comes to overhead, it's mostly space. optional

always takes whatever it holds, plus the boolean, plus any additional padding to do the alignment. For example, std::string

it is often implemented as 24 bytes, 8-byte aligned. Then it optional<string>

will be equal to 25 bytes, but due to alignment it will be equal to 32 bytes. For a primitive type (int or enum), it usually doubles the amount of memory required from 4 to 8 bytes, or something similar.

As far as performance is concerned, there won't be any difference in cache externalities in this case (if the optimizer is smart). Comparison std::string

to an empty string literal would probably be optimized to call std::string::empty

(you should probably write it that way) that just checks if an integer is zero, which is the same as your comparison for Color

, which is basically the same as checking that whether the boolean value is zero.



I said what I like optional

; I think it conveys the intent of the code more clearly. However, if you have very obvious sentinel meanings, then perhaps it is less valuable.

In some cases, you can get your cake and eat it with compact options too: https://github.com/akrzemi1/compact_optional . It has an identical front-end to the normal sub, but you give it a watchdog and it uses that watchdog to store the missing state. May not work well with all classes.

+4


source


While std :: optional can carry the overhead of an extra boolean value, it also serves a descriptive purpose here: it fully expresses the concept you are trying to put into the code, making that code straight forward to the reader, which continues. Since this is a user interface and the logical overhead is relatively small, I would say for this.



I dispute the claim that std :: optional was only meant to return a function. It would be ridiculously limited.

+1


source







All Articles