Search code examples
c++notnullstdoptional

When would you use optional<not_null<T*>>


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.


Solution

  • 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({});