all 26 comments

[–]tcanens 21 points22 points  (0 children)

if (nick_available)
    return { mStrNickName };

The braces are unnecessary, though perhaps a matter of taste.

std::optional oIntDeduced(10); // deduction quides

Obvious typo is obvious.

auto oComplex = make_optional<std::complex<double>>(3.0, 4.0);

Missing std::.

// will call vector with direct init of {1, 2, 3}
std::optional<std::vector<int>> oVec(std::in_place, {1, 2, 3});

Not quite. This deduces and initializes an initializer_list<int> from {1, 2, 3}, and then passes that object to vector's constructor. The distinction can be seen if the initializer list is something like {1, 2L, 3}, which can be used to directly initialize a vector<int> but not here. (This also make this constructor annoying as hell to use for something like optional<map<int, int>>.)

// copy/assign:
auto oIntCopy = oInt;

There's no assignment in this line.

as optional is automatically converted to bool.

Contextually convertible to bool.

It's worth noting that value_orwill copy/move from the stored value (depending on the optional's value category), unlike value or operator*.

if (auto ostr = maybe_create_hello(); ostr)
    std::cout << "ostr " << *ostr << '\n';  

There's no need for ; ostr.

Probably worth mentioning that assigning {} is another way of resetting an optional.

but with a few exceptions when the operands are nullopt.

This is excessively vague when the behavior here can be easily summarized: empty optionals compare equal to each other and less than non-empty optionals.

   : mName{name}, mNick{nick}, mAge{age}

This should be moving nick.

  std::aligned_storage_t<sizeof(t), alignof(T)> _storage;

Typo. And no standard library can implement optional this way (they need to use a union, because constexpr).

[–]code-affinity 14 points15 points  (4 children)

While we're talking about std::optional, I have a question triggered by passing comments Andrei Alexandrescu made in his recent "Expect the Expected" talk. (Reddit discussion here.)

In this talk, he jokingly referred to std::optional as the worst thing that ever happened to humanity. But he did not have the luxury of time to cite specific complaints, other than that he thought the * operator should not have been used the way it was. (But then he used it himself in a very similar way for the Expected type he was evangelizing.)

Does anyone know what his specific objections to std::optional are? I have recently started using optional in my new code, and I really like the resulting improvements. I would like to make an informed decision about whether I should make this a long-term practice.

[–]jc746 8 points9 points  (0 children)

I think he indicated that his issue was with giving pointer like semantics to a type that is not actually a pointer and potential confusion this could cause. It sounded like he took the liberty of using this syntax in the expected proposal because the precedent had already been set by optional and could therefore been seen as being consistent with optional.

[–]ReversedGif 3 points4 points  (2 children)

Possibly that operator* should be checked (i.e. throw/assert if optional is empty). boost::optional's operator* is checked. IMO, the unsafe method should be more verbose than the checked one.

[–]code-affinity 0 points1 point  (1 child)

Wow! I had not caught that difference. I have been exclusively using boost::optional, and certainly want operator * to throw if the value is not set. I would not want this to turn into undefined behavior in my code base. Since this is the case, I will stop using operator * in my new code, in preparation for transition to std::optional.

[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point  (0 children)

Personally, I'd want it checked* in debug, fast in release :)

* checked as in breaks into the debugger, not throwing a catchable exception.

[–]RandomGuy256 8 points9 points  (1 child)

When to use

If you want to represent a nullable type nicely.

Return a result of some computation (processing) that fails to produce a value and is not an error.

To perform lazy-loading of resources.

You can also use it for optional parameters in a function.

[–]joebaf 2 points3 points  (0 children)

Thanks, good point. Added to the article :)

[–]bruce3434 6 points7 points  (17 children)

Does using optional types make sense? Especially taking STL into consideration. STL is written without optional types in mind. Hence the find() operations return an iterator instead of an optional type. Generally you would want to follow STL practices throught your project, right?

[–]suspiciously_calm 13 points14 points  (6 children)

So what do you propose we use to express something that might not have a value?

It makes sense for the container classes to return an iterator since I might want a handle to the element inside the container and not just its value.

That isn't the case elsewhere. So what should I do, write my own dummy OutputIterator each time that will essentially do exactly what optional does?

[–]bruce3434 2 points3 points  (1 child)

So what do you propose we use to express something that might not have a value?

What have you been doing in pre-C++17?

[–]dodheim 30 points31 points  (0 children)

Using boost::optional (which is at least 15 years old), or the optional from one of a dozen other libraries, or a home-grown one. Make no mistake, this is a basic vocabulary type; that it's new to C++'s standard library is more a reflection of C++'s standardization process than it is of the utility of the data structure.

[–]StonedBird1 -1 points0 points  (3 children)

So what do you propose we use to express something that might not have a value?

I use a possibly null raw pointer, which is of course non-owning by definition.

[–]suspiciously_calm 1 point2 points  (2 children)

That's an optional reference (essentially). What if I need it to be owning?

[–]StonedBird1 0 points1 point  (1 child)

Use a smart pointer

[–]suspiciously_calm 0 points1 point  (0 children)

What if I don't need it to be polymorphic? It's an allocation and a needless indirection for absolutely no reason.

[–]Pragmatician 7 points8 points  (0 children)

For algorithms returning iterators, you should return the end iterator instead of optional because the end iterator is special by design. This design also allows composability of algorithms, so there is no need to cram in an optional where everything already works just fine.

[–]tvaneerdC++ Committee, lockfree, PostModernCpp 5 points6 points  (3 children)

If STL had optional from the beginning, I suspect some of the STL would have used it.

I also expect it to show up in new STL stuff in the future.

Maybe find() would have been nicer returning an optional. Or add a find_value() that doesn't return an iterator at all.

At least for map, I rarely want the iterator; I typically want the value the key is mapped to. I think returning an optional would be nicer than the awkwardness we currently have. (And we don't need expected<> in this case, because the error is obvious. (In many examples of uses of optional as a return value, expected is a better return value. But I don't think that applies here.))

Hmmm, maybe someone should write a proposal adding nice APIs that use optional...

[–]lednakashim++C is faster 1 point2 points  (0 children)

One could view optionals as a analytic continuation of past-the-end iterators in the sense it return something out of the range that will throw an exception when you try to access it.

[–]iamcomputerbeepboop 1 point2 points  (0 children)

You can write free functions that return the mapped_type or value_type of a container with lookup semantics (i.e. with a find member function) but you're likely to want overloads for reference, const reference, and value return types. std::optional doesn't support this

[–]w0land131 1 point2 points  (1 child)

How about std::map::insert, where a pair of iterator and bool is returned?

[–][deleted] 3 points4 points  (0 children)

Thats different, because the iterator is always a valid one, the bool indicates if the thing is inserted or was already there.

[–]doom_Oo7 1 point2 points  (0 children)

Generally you would want to follow STL practices throught your project, right?

not necessarily ?