C++ Logo

std-proposals

Advanced search

[std-proposals] Use optional<T> as though it were T

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sun, 25 Jun 2023 12:53:06 +0100
Let's say you started out writing a program with a global object:

    T g_obj;

Now let's say that over 15 years, your program gets a lot bigger and
so now you want to start conserving RAM. This global object has a
constructor that allocates 100 megabytes of RAM, and so you decide to
delay its construction until it's actually needed, so you change it
to:

    optional<T> g_obj;

Of course this would mean that you need to edit the code everywhere so
that you have "g_obj->" instead of "g_obj.", unless of course you were
to do:

    optional<T> g_optional_obj;
    T &g_obj = *g_optional_obj.operator->();

Strictly speaking this is undefined behaviour but it will work fine so
long as you make sure to 'emplace' the object before using 'g_obj'.

But what if we could standardise a way of doing this that doesn't
result in undefined behaviour? One possibility could be code that
works something like as follows:

    #include <cassert> // cassert
    #include <optional> // optional, bad_optional_access

    using std::optional;
    using T = int;

    optional<T> g_optional_obj;

    #define NDEBUG // Try commenting this out

    #ifdef NDEBUG
    # define g_obj ( g_optional_obj.has_value() \
                       ? g_optional_obj.value() \
                       : throw std::bad_optional_access() )
    #else
    # define g_obj ( assert(g_optional_obj.has_value()), \
                       g_optional_obj.value() )
    #endif

    // ============================== Here comes the test code:

    #include <iostream>
    using std::cout, std::endl;

    int main(void)
    {
        g_optional_obj.emplace(6);
        g_obj = 7;
        cout << g_obj << endl;
    }

Try it up on GodBolt: https://godbolt.org/z/cz96bMW5j

If we were to standardise a way of getting a "T&" from an
"optional<T>&" at compile time, then the syntax could be something
like as follows:

    std::optional<T> g_optional_obj -> g_obj;

Or perhaps avoid a change to the syntax of the core language, and
split it into two lines:

    std::optional<T> g_optional_obj;
    auto g_obj = g_optional_obj.compile_time_ref();

Or another possible syntax:

    std::optional<T> g_optional_obj;
    auto g_obj = std::optional_compile_time_ref( g_optional_obj );

Received on 2023-06-25 11:53:18