Search code examples
c++c++17option-type

std::optional - construct empty with curly brackets {} or std::nullopt?


I thought that initializing a std::optional with std::nullopt would be the same as default construction.

They are described as equivalent at cppreference, as form (1)

However, both Clang and GCC seem to treat these toy example functions differently.

#include <optional>

struct Data { char large_data[0x10000]; };

std::optional<Data> nullopt_init()
{
  return std::nullopt;
}

std::optional<Data> default_init()
{
  return {};
}

Compiler Explorer seems to imply that using std::nullopt will simply set the one byte "has_value" flag,

nullopt_init():
    mov     BYTE PTR [rdi+65536], 0
    mov     rax, rdi
    ret

While default construction will value-initialize every byte of the class. This is functionally equivalent, but almost always costlier.

default_init():
    sub     rsp, 8
    mov     edx, 65537
    xor     esi, esi
    call    memset
    add     rsp, 8
    ret

Is this intentional behavior? When should one form be preferred over the other?


Update: GCC (since v11.1) and Clang (since v12.0.1) now treat both forms efficiently.


Solution

  • In this case, {} invokes value-initialization. If optional's default constructor is not user-provided (where "not user-provided" means roughly "is implicitly declared or explicitly defaulted within the class definition"), that incurs zero-initialization of the entire object.

    Whether it does so depends on the implementation details of that particular std::optional implementation. It looks like libstdc++'s optional's default constructor is not user-provided, but libc++'s is.