3 ms·
optional<T &> is not valid in C++ [1] [1]: https://en.cppreference.com/w/cpp/utility/optional https://en.cppreference.com/w/cpp/utility/optional
by cmovq 4y ago
optional<T &> is not valid in C++ [1]
[1]: https://en.cppreference.com/w/cpp/utility/optional https://en.cppreference.com/w/cpp/utility/optional
- tialaramex 4y agoBecause C++ doesn't have the Guaranteed Niche Optimization C++ optional<T&> would be pretty miserable whereas Rust's Option<&T> is exactly what you want. In practice C++ APIs where you'd naturally write Option<&T> use pointers and are consequently easier to use wrong, this is an example of Rust actually providing improved safety technology (whereas many commonly identified safety differences like bounds checking in Rust are cultural differences)
- gpderetta 4y agoC++ could specify that std::optional<T&> is valid and implementations could implement the Guaranteed Niche Optimizations. This is not a theoretical possibility: boost.optional (which std::optional is based on) does exactly that. The reason std::optional doesn't allow references is because someone in the commitee objected that some users might be confused by the assignment semantics (will assignment rebind or assign through the reference?) and the proposal authors dropped it instead of spending too long discussing pro and cons. It can still be easily re-added if the commitee wants to. No need to drag the rust in the discussion.
- tialaramex 4y agoI'm pretty sure you can't express the actual guarantee in C++ because of inadequacy in the design of the C++ type system, which has caused problems elsewhere recently too. You could easily hack std::optional<T&> specifically - and several people have, not just Boost - however C++ has a long history of "clever" hacked specializations that turn out to be disastrous so that isn't attractive. It's called generic programming for a reason. The fact that it looks like you might be able to rebind or assign through the reference is a design goof. To the extent that std::optional is just the Boost type with a sticker on, that's a goof by the people behind the Boost type. However, they were hemmed in by inadequate language functionality, WG21 has the option to go fix the language, and they didn't do that, so the blame for that goes to them and not Boost. Having now standardised std::optional with the design goof, there is no attractive route forward. The correct answer to "someone's" question by the way should be neither and that's the goof in std::optional, assignment to a Maybe type is assignment to the Maybe type you can't assign a Foo to a Maybe<Foo>, imagine if C++ also thought this ought to work for std::vector<int> ...
- gpderetta 4y ago> I'm pretty sure you can't express the actual guarantee in C++ because of inadequacy in the design of the C++ type system, which has caused problems elsewhere recently too. Far from being inadequate, it is pretty trivial to do so: https://gcc.godbolt.org/z/n4vK4jeaG https://gcc.godbolt.org/z/n4vK4jeaG . You still need to special-case references because they are not actual C++ objects and need special handling.
- tialaramex 4y agoIt's not about whether you can implement this hack - obviously numerous people have done that - it's about the type system itself. Here's a question for you, what is the type of a do expression which just returns from the function you're in ? I mean, it's just an expression, we don't have to actually evaluate it we can ask what the type is... can't we ? As far as I can see, C++ does not have a suitable type, which is why e.g. some C++ functions end up with a special attribute to signify that they won't actually return. I can see why this type would be unsettling, it's like when the ancient Greeks didn't like zero and so they initially refused to treat it as an integer. The type you want here is the additive identity. Because the type system doesn't really understand additive identity, it's a struggle to write such a guarantee - not as a hack but in the type system.
- gpderetta 4y agoI'm sorry, but I have no interest in playing a game of True Scotsman Chases The Goalpost. Even if I did, I have no idea what you are trying to say. edit: sorry that was a bit harsh. Can you try to explain what property is C++ missing exactly to implement the Niche optimization? I suspect you are trying to say that C++ lacks proper sum types (which is true), but that's really orthogonal to the optimization.
- tialaramex 4y agoI don't think it is orthogonal, because it means we have to resort to this hack everywhere and we already know that specialization hacks end badly. Let me briefly explain why I've thought about this pretty hard. Over in Rust I really want to make BalancedI8, BalancedI16 and so on, types which are exactly the same as the ordinary signed integers of each size, except, their smallest possible value (e.g. i8::MIN ie -128) is gone (so BalancedI8::MIN is -127), leaving them nicely balanced between negative and positive, their abs() method doesn't have a hidden surprise - and of course this offers the smallest possible niche so Option<BalancedI8> fits in a single byte. I obviously can in some sense do that today and I did -- the types are published, but today I can't use them from stable Rust, these types require a (deliberately permanently unstable) Rust-internal attribute telling the compiler about the niche, and they also can't be safely constructed (obviously I provide a safe function to make one, but you can't just conjure one into existence without calling the function, yes it gets inlined but that's not very ergonomic) This is not a desirable state of affairs. As usual in Rust, the reason for the perma-unstable feature flag is that there's a germ of an idea of the Right Thing™ and so the eventual removal of the feature flag is predicated on obsoleting the feature by having the Right Thing™ in the language itself, not stabilization of the existing flag. Rust types which provide a niche will some day remove the feature flag and instead be defined using the language in the now-natural way, yet to end users nothing seems to have changed. The backwards incompatible change lives only inside private internals of the standard library so it's fine. In this case that Right Thing turns out to be Pattern Types. What if we actually just tell the type system what's going on here about which values a type can actually have versus which ones are niches by providing a Pattern for the type. So it knows everywhere in the language that -128 may be a valid i8 but it's not a valid BalancedI8 because the pattern doesn't match. However, Pattern Types might take quite a while to produce even a basic MVP implementation that can be stabilized, and so for a while I was pushing to get some way to just have what I want stabilized, sooner - without all of Pattern Types, and then replace it later using Pattern Types. But I was persuaded that actually this is poison, if you introduce my hack as a stable feature then it cannot ever actually be translated into a Pattern Type in user's code, so it becomes this weird orphan feature, "tialaramex's stupid niche type hack" forever even though all new types would use Pattern Types to achieve a much superior outcome, there's no known way to fix any of my hacked types. So in effect you're making the arguments I was making like a month ago, that I now see are hopeless, which is why I'm asserting so vigorously that this is just a hack and what you should aim for instead is to explain to the type system what's going on even though that's harder. The question about decltype ( do { return; } ) is pertinent because that type is to a type system like zero is to the integers in Z. I hope I spelled that correctly - I can't Godbolt it because do expressions are a speculative design, but hopefully you get the idea. This problem is easier to express with a do expression than with other C++ where the same problem arises but is harder to exhibit.
- dralley 4y ago> It can still be easily re-added if the commitee wants to. Yes but the earliest that could happen is, like, 2026 or perhaps 2027. Because you have to wait for both a new std revision to become available.