When would you use optional<not_null<T*>>
Asked Answered
B

3

6

I was reading References, simply and got to the part talking about using optional references. One of the reasons Herb gives to avoid optional<T&> is because those situations can be "represented about equally well by optional<not_null<T*>>"

I'm confused about when you would ever want optional<not_null<T*>>. In my mind, the optional cancel's out the not_null, so why wouldn't you just use a raw pointer in a case like this.

Burglar answered 26/9, 2022 at 15:50 Comment(2)
if you want to interact with code that expects optionals and not_nulls. If it's all your own code, then you can choose to just use T*Treenware
Replacing optional<T&> by T* has the disadvantage that T* is not meaningful, it could be owning or not, it might be a single object or an array. :-/Rampant
O
1

T* doesn't have any member functions, whereas optional<not_null<T*>> has a bunch.

What I'd like is to be able to compose functions like

auto result = maybe_A()
    .transform(A_to_B)
    .and_then(B_to_opt_C)
    .or_else({});

which should be equivalent to

optional<A&> first = maybe_A();
optional<B&> second = first.transform(A_to_B);
optional<C&> third = second.and_then(B_to_opt_C);
C result = third.or_else({});

With pointers, we can't do that as one expression.

A* first = maybe_A();
B* second = first ? A_to_B(*first) : nullptr;
C* third = second ? B_to_opt_C(*second) : nullptr;
C result = third ? *third : {};

Whereas at least with optional<not_null<T*>> we can adapt our functions

optional<not_null<A*>> first = maybe_A();
optional<not_null<B*>> second = first.transform([](not_null<A*> a){ return &A_to_B(*a); });
optional<not_null<C*>> third = second.and_then([](not_null<B*> b){ return B_to_opt_C(*b); });
C result = third.or_else({});

a.k.a

auto result = maybe_A()
    .transform([](not_null<A*> a){ return &A_to_B(*a); })
    .and_then([](not_null<B*> b){ return B_to_opt_C(*b); })
    .or_else({});
Oho answered 26/9, 2022 at 16:42 Comment(0)
C
0

For the same reason you might want optional<optional<T>>.

At some level you code operates with value which might exist or not. Hence optional.

Value type of that optional value can be T, not_null<T>, nullable<T> or anything else, it does not matter to that code. It just checks for value existence and possibly pass it to some other fucntions.

Those functions represent some other level of code. They can accept their arguments from functions unwrapping optional, taking address of its member, etc. They have own preconditions: argument should be pointer, and it should not be null. Hence not_null<T*>.

In the end, you get optional<not_null<T*>>, because some code expects not_null<T*>, and other wants to take whatever first want and wrap it in optional.

Clemente answered 26/9, 2022 at 16:5 Comment(0)
P
0

It's important to remember the context of the suggestion. The problem that optional<T&> would solve is cases where a user may or may not have a T, but if they do, they want you to modify it in some way while still retaining access to it. And that you want to use the optional interface to handle the "not a value" case.

So if you want to use the optional interface (and therefore T* isn't an option), and optional<T&> is not available by fiat, then you could use optional<T*> as a substitute. But technically that means that the optional could have a "value" which is nullptr. So instead, you use a type that explicitly forbids nullptr: optional<not_null<T*>>.

Basically, the suggestion is there to say that you can get the effects of optional<T&> without the problems that such a type tends to bring.

Protuberancy answered 26/9, 2022 at 16:11 Comment(1)
std::optional<std::reference_wrapper<T>> would be another alternative.Rampant

© 2022 - 2024 — McMap. All rights reserved.