DEV Community

Pierre Gradot
Pierre Gradot

Posted on • Edited on

3

std::optional from C++17 vs custom type for optional value

C++17 has introduced a very useful template class, std::optional:

The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.

I have been using this feature a lot since 2017, even for a simple optional integer. Today, I got curious: is this template type effective compared to an equivalent type that I would write myself to achieve the same behavior?

Good question, thank you 😆

Let's try to write some code and use Compiler Explorer to compare both solutions: std::optional vs custom type for optional value.

First, let's define two equivalent types:

#include <optional>

using StdOptionalInt = std::optional<int>;

struct CustomOptionalInt {    
    bool has_value;
    int value;
};
Enter fullscreen mode Exit fullscreen mode

Then, let's write two functions that test if the value is available and return either this value (if available) or a default value (if not available):

int getStdOptional(const StdOptionalInt& o) {
    return o.has_value() ? o.value() : 0;
}

int getCustomOptional(const CustomOptionalInt& o) {
    return o.has_value ? o.value : 0;
}
Enter fullscreen mode Exit fullscreen mode

Finally, compile the code with Compiler Explorer and compare the output assembly codes (you can try by yourself here):

with_compiler_explorer

Yeah! 😃 The 2 functions generate the same assembly code. There is no difference of performance. Notice the static assertion at the end of the source code: because the code compiles, it means the footprint are the same. The behavior is the same with gcc and clang for x86-64.

As a conclusion, std::optional is as efficient as a custom type to represent an optional integer value. Don't implement your own type, simply use the standard type. You may even get better performance using std::optional, as explained on cppreference:

As opposed to other approaches, such as std::pair<T,bool>, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.


By the way, if you happen to speak French:

  • I wrote an article to explain to how to use std::optional.
  • I made a video to present Compiler Explorer.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (2)

Collapse
 
sandordargo profile image
Sandor Dargo

Stunning that the generated code is the same. Have you had a look into one od the standard implementations?

github.com/gcc-mirror/gcc/blob/mas...

Collapse
 
pgradot profile image
Pierre Gradot • Edited

To be honest, I wasn't surprised. I was not expecting the exact same code but I was expecting something similar, for various reasons:

  1. has_value() is just about returning a bool. No reason to cost more than a simple access to member variable.
  2. value() is just a test to decide to return the value or throw an exception. Nothing fancy here.

We may expect the exception to cost a more than a simple access to a member variable. So why ?

This leads me to reason 3. Over the years (and hours spent in Compiler Explorer, you definitively must use it!), I have learned something important: modern compilers are amazing at optimizing code. Way much better that you and me.

And here, take a better look at the code: it tests if the std::optional has a value and get the value conditionally. The compiler then know the exception cannot be thrown and optimizes that out.

Change the code and get a completely different result:

int getStdOptional(const StdOptionalInt& o) {
    return o.value();
}
Enter fullscreen mode Exit fullscreen mode

This generates all the code to handle the exception and it's a lot a code.

I something take a look at the source of standard features. I simply to a CTRL+click in my IDE. std::optional seems crazy but it does much more than my custom type. It is made by and for the standard library developers. And this leads me to reason 4 : these developers are way better than you and me to create powerful, flexible, easy-to-use, robust code ;)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more