Seems like the daily anti c++ post
Seems like the daily anti c++ post
> optional is unsafe in idiomatic use cases? I’d like to challenge that.
std::optional<int> x(std::nullopt);
int val = *x;
Optional is by default unsafe - the above code is UB.Anyway safety checked modes are sufficient for many programs, this article claims otherwise but then contradicts itself by showing that they caught most issues using .. safety checked modes.
I recently had a less wild but similarly baffling experience on an embedded-but-not-small device. Address 0 was actually a valid address. We were getting a HardFault because a device driver was dereferencing a pointer to an invalid but not-null address. Working backwards, I found that it was getting that invalid address not from 0x0 but rather from 0xC… because the pointer was stored in the third field of a struct and our pointer to that struct was null.
foo->bar->baz->zap
Foo = 0, &bar = 0xC, baz = invalid address, *baz to get zap is what blew up.No, it won't. https://gcc.godbolt.org/z/Mz8sqKvad
The problem is not nullopt, but that the client code can simply dereference the optional instead of being forced to pattern-match. And the next problem, like the other guy mentioned above, is that you cannot make any claims about what will happen when you do so because the standard just says "UB". Other languages like Haskell also have things like fromJust, but at least the behaviour is well-defined when the value is Nothing.
It's not a pointer.
I also agree with them: I am pro-C++ too, but the current standard is a fucking mess. Go and look at modules if you haven't, for example (don't).
You can't magically make all the member functions on std::vector safe after a move for example unless the moved from vector allocates itself a new (empty) buffer, which kills the performance benefits.
It's all by design.
Reusing of a moved from object only requires assignment and destruction to be well behaved.
The std library containers give you extra guarantees (a moved from object is effectively the same as a default constructed one), but the _language_ imposes no such requirements on your types
It's perfectly allowed by the language for the .size() member of your own vector type to return a random value after it's been moved from because you wanted to save 1 CPU instruction somewhere.
An empty std::vector does not require that any buffer is allocated. It just has a null data pointer.
It's not true that the only sensible choice for a moved-from object be equivalent to the defaulted constructed one.
If your move constructor doesn't exist then the copy constructor gets called under the language rules, so the sensible default is actually a copy.
Everything else is an optimisation that has a trade-off
A conforming implementation of std::list, for example, can have a default constructor and a move constructor that both allocate a sentinel node on the heap, which is why none of the constructors are noexcept.
If you don't allocate a sentinel on the heap, then moving std::list can invalidate iterators (which is what GNU stdlibc++'s implementation chooses).
It's a trade off.
But yes, the implication of C++ move semantics is that every movable object must also define an “empty” (moved-from) state, so you cannot have something like a never-null unique ptr.
Specifically, it is not allowed for the moved-from object to be inconsistent or to say “using it in any way is UB”, because its destructor will run.
I beg to differ. Humans are fallible. Static analysis of C++ cannot catch all cases and humans will often accept a change that passes the analyses.
You're ignoring how static analysis can be made to err on the side of safety rather than promiscuity.
Specifically, for optional dereferencing, static analysis can be made to disallow it unless it can prove the optional has a value.
Ho ho ho good one.
> The following code for example, simply returns an uninitialized value:
#include <optional>
int f() {
std::optional<int> x(std::nullopt);
return *x;
}tl;dr: use-after-move, or dereferencing null.