Std::Optional - Construct Empty with {} or Std::Nullopt

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

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.

When explicitly initializing std::optional's, should I use nullopt?

They both have the same effect - I would prefer the simplest form (KISS), but it is subjective, pick one and be consistent. You may also wish to be consistent with how you treat other objects in your code, do you normally rely on default initialization (e.g. int i{} vs int i{0})?

Personally when I see redundant code, like initalizing an object to its default value explicitly, it does reduce my confidence in the author by a slight margin - does the author really understand what he is doing and trying to be extra safe / explicit / readable, or was he simply too lazy to read the documentation? It makes me wonder, does the author understand what happens when he writes std::vector<std::optional> v(n), or more complex examples? If this is a documented decision, in a coding style, then all is fine, I can understand the need to improve readability.

Why do we need std::nullopt

No.

This is a perfectly valid way to default-construct an optional.

Even for assignment, you can copy-assign a default-constructed optional with = {} instead of using std::nullopt:

cppreference actually says as much:

The constraints on nullopt_t's constructors exist to support both op = {}; and op = nullopt; as the syntax for disengaging an optional object.

… as does the original proposal for the feature:

Note that it is not the only way to disengage an optional object. You can also use:

op = std::nullopt;

You might ask yourself why, then, std::nullopt exists at all. The proposal addresses this, too:

it introduces redundancy into the interface

[similar example]

On the other hand, there are usages where the usage of nullopt cannot be replaced with any other convenient notation:

void run(complex<double> v);
void run(optional<string> v);

run(nullopt); // pick the second overload
run({}); // ambiguous

if (opt1 == nullopt) ... // fine
if (opt2 == {}) ... // illegal

bool is_engaged( optional<int> o)
{
return bool(o); // ok, but unclear
return o != nullopt; // familiar
}

While some situations would work with {} syntax, using nullopt makes the programmer's intention more clear. Compare these:

optional<vector<int>> get1() {
return {};
}

optional<vector<int>> get2() {
return nullopt;
}

optional<vector<int>> get3() {
return optional<vector<int>>{};
}

In short, std::nullopt can be useful, but in your case it simply comes down to style.

What are the advantages/disadvantages of std::optional over nullptr?

The sole job of std::optional is to extend the type domain by an additional "null" value. Every pointer type T* already has a value considered "null" - nulltpr.

Thus, it's not a good idea to compare those two directly, because they answer different questions. Sometimes it's important to differentiate between "no result" and "null result"1 (which is one of the possible interpretations), sometimes it's not. You should use whichever fits your needs.

Now if the only reason the code returned a pointer was to make use of the implicit pointer nullability, then the proper solution would be to change it to return std::optional<SomePointer> (or perhaps std::optional<std::reference_wrapper<SomePointer>>) instead, but that's not what you asked about.


1 Of course at this point it's also worthwhile to consider something like e.g. struct NoResult {}; using Result = std::variant<NoResult, SomePointer*>; to make it even more explicit.

How to assign nothing to std::optionalT?

I wouldn't say there is one "standard" way to assign nothing to std::optional.

If you read the proposal for std::optional ("A proposal to add a utility class to represent optional objects") the authors present two ways, in this order:

We put the extra requirements in the standardese to make sure that the following syntax works for resetting the optional:

op = {};

We consider that this will become a common idiom for resetting (putting into default-constructed state) values in C++

Note that it is not the only way to disengage an optional object. You can also use:

op = std::nullopt;

std::optional::reset did not exist at the time of writing that proposal, so it was not mentioned, however it is also a valid way to do it.

How do I obtain a non-empty optional with the value inside it being default constructed?

The easiest way is

std::optional<std::vector<int>> ov = {{}};

alternatives include

std::optional<std::vector<int>> ov(std::in_place);

sadly there is no way to do this with assignment instead of construction; {{}} is ambiguous, and the std::in_place constructor is explicit.

There you have to call ov.emplace().

You could create a helper object;

struct non_empty {
template<class T>
constexpr operator std::optional<T>()const{ return {{}}; }
};

then

ov = non_empty{};

does the job.



Related Topics



Leave a reply



Submit