• std::optional - avoid data copying for RVO

    From David Brown@3:633/10 to All on Fri Apr 24 14:33:50 2026
    I am making some type-safe C++ wrappers for some older C APIs. In one
    case I have a C function that is approximately this (returning 1 if the
    data was successfully copied) :

    extern int try_to_get_data(void *);

    and it is used to get data in a simple struct like :

    struct Data {
    int a;
    float b;
    char c[40];
    };


    A simple and efficient wrapper would then be:

    bool get1(Data& data) {
    return try_to_get_data(&data);
    }

    That does exactly the same thing, but it type-safe.

    However, my preference is that returns from functions are returned,
    rather than passing pointers and references around. The natural
    signature here is thus to return std::optional<Data>. To possible implementations are :

    std::optional<Data> get2() {
    if (Data d; try_to_get_data(&d)) return d;
    return std::nullopt;
    }

    std::optional<Data> get3() {
    std::optional<Data> so;
    if (Data d; try_to_get_data(&d)) so = d;
    return so;
    }

    But these have significant inefficiencies. The local "Data" object
    needs to be created on the stack and its address is passed to the
    external function - then on success, it is copied from the local stack
    object into the caller's stack where the returned std::optional value is
    held. On failure, the whole returned std::optional is cleared - not
    just the "engaged" boolean inside the std::optional implementation.

    I can make my own much-simplified optional type with additional
    accessors to get the address of the payload field and to allow directly changing the "engaged" field without affecting the payload. Then I can
    have :

    Simple get5() {
    Simple so;
    so.set_engaged(try_to_get_data(&so.value()));
    return so;
    }

    This gives me optimal generated code.

    Is there anything I am missing from std::optional that would let me have
    this kind of direct access? I suspect not, since that kind of access
    bypasses the safety of std::optional of only having access to the
    payload if it is engaged. But avoiding the copying makes a big
    difference to the efficiency for large optionals.

    And is there a good reason for clearing out the payload here when
    returning nullopt? Or is that just an inefficiency of the gcc standard
    C++ library implementation?


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Sun Apr 26 19:47:23 2026
    Am 24.04.2026 um 14:33 schrieb David Brown:

    But these have significant inefficiencies.ÿ The local "Data" object
    needs to be created on the stack and its address is passed to the
    external function - then on success, it is copied from the local stack object into the caller's stack where the returned std::optional value is held.ÿ On failure, the whole returned std::optional is cleared - not
    just the "engaged" boolean inside the std::optional implementation.

    Move and it's cheap.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Mon Apr 27 05:47:12 2026
    On Fri 4/24/2026 5:33 AM, David Brown wrote:

    And is there a good reason for clearing out the payload here when
    returning nullopt?ÿ Or is that just an inefficiency of the gcc standard
    C++ library implementation?


    My guess would be that it is caused by your payload class having no user-defined constructor. This causes any kind of default initialization
    to turn into value-initialization requiring everything to be zeroed out.

    If you add an explicit "do nothing" user-defined constructor this
    behavior will probably go away. Of course, if the struct is declared in
    the C side, this might require jumping through some hoops...

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Mon Apr 27 06:10:06 2026
    On Mon 4/27/2026 5:47 AM, Andrey Tarasevich wrote:
    My guess would be that it is caused by your payload class having no user-defined constructor. This causes any kind of default initialization
    to turn into value-initialization requiring everything to be zeroed out.

    If you add an explicit "do nothing" user-defined constructor this
    behavior will probably go away. Of course, if the struct is declared in
    the C side, this might require jumping through some hoops...

    ... although the above does not answer the question of why GCC would try
    to apply _any_ kind of initialization to the inactive payload in `std::optional` (even if it helps to suppress it).

    --
    Best regards,
    Andrey


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Mon Apr 27 19:05:24 2026
    On 27/04/2026 15:10, Andrey Tarasevich wrote:
    On Mon 4/27/2026 5:47 AM, Andrey Tarasevich wrote:
    My guess would be that it is caused by your payload class having no
    user-defined constructor. This causes any kind of default
    initialization to turn into value-initialization requiring everything
    to be zeroed out.

    If you add an explicit "do nothing" user-defined constructor this
    behavior will probably go away. Of course, if the struct is declared
    in the C side, this might require jumping through some hoops...


    Exactly!

    ... although the above does not answer the question of why GCC would try
    to apply _any_ kind of initialization to the inactive payload in `std::optional` (even if it helps to suppress it).


    Yes, that's what I am curious about.

    For now, at least, I've made a "home-made" optional that works fine for
    the purpose. It doesn't have all the features of the standard type in
    the face of different requirements for moves, copies, etc., but it's
    fine for POD structs pulled in from the C world. (I don't think they
    are called POD any more with that latest C++ standards, but I think it
    is clear what I mean.)


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)