5 ms·
I’m very much pro c++, but anti c++’s direction. > optional is unsafe in idiomatic use cases? I’d like to challenge that. std::optional<int> x(std::nullop
by maccard 11mo ago
I’m very much pro c++, but anti c++’s direction.
> 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.
- TinkersW 11mo agoThat is actually memory safe, as null will always trigger access violation.. 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.
- steveklabnik 11mo agoIt is undefined behavior. You cannot make a claim about what it will always do.
- AlotOfReading 11mo agoAs a fun example, I worked on a safety-critical system where accessing all-bits-zero pointers would trigger an IRQ that jumped back to PC + 4, leaving the register/variable uninitialized. Great fun was had any time there was LR corruption and CPU started executing whatever happened to be next in memory after function return.
- tonyarkles 11mo agoHahahaha well that behaviour is certainly fun! 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.
- maccard 11mo ago>null will always trigger access violation.. No, it won't. https://gcc.godbolt.org/z/Mz8sqKvad https://gcc.godbolt.org/z/Mz8sqKvad
- TinkersW 11mo agoOh my bad, I read that as nullptr, I use a custom optional that does not support such a silly mode as "disengaged"
- canyp 11mo agoHow is that an optional then? 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.
- maccard 11mo agoWhat do you return if there is no value set? That’s the entire point of optional.
- wild_pointer 11mo agoYou didn't read this, did you? https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/ https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/ It's not a pointer.
- on_the_train 11mo agoBut using the deref op is deliberately unsafe, and never used without a check in practice. This would neither pass a review, nor static analysis.
- canyp 11mo agoGP picked the less useful of the two examples. The other one is a use-after-move, which static analysis won't catch beyond trivial cases where the relevant code is inside function scope. 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).
- nly 11mo agoNo type is safe to use after a move, unless it's documented to put itself in to a well defined state and says as such 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.
- surajrmal 11mo agoI believe it's actually the opposite. You're supposed to be able to reuse objects that were moved from unless otherwise documented, although it may require reinitializing it explicitly. A moved from vector is valid to reuse. Although the standard doesn't specify what state it will be in, all major standard library implementations return it to the default constructed state which doesn't require an allocation.
- nly 11mo agoRead what I wrote again. I said all member functions. 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.
- nly 11mo agoA static analyzer could easily tell you to use the monadic member functions instead of a raw dereference.