How to access contained value in C++23 std::optional without writing identity boilerplate?
Asked Answered
B

1

9

I am playing with C++23 std::optional additions, but I can not figure out how to elegantly access the value of object if optional is active.

I know I can use if, but that is so C++20.

I really like C++23 API changes, but I can not figure out how to skip the boilerplate of implementing identity. For example:

#include <functional>
#include <iostream>
#include <optional>

void print(std::optional<std::string>& name) {
    name.transform([](std::string& x) {
        std::cout << x << std::endl;
        // I just want to print, without modifying optional, but next line is required
        return x;
    });
}

int main() {
    std::optional<std::string> name{{"Bjarne"}};
    print(name);
}

It almost feels like std::optional is missing invoke member function.

note: in my example I do not chain anything else after transform, but that is for brevity, I care about optional not being modified.

Bronwyn answered 7/2, 2022 at 21:58 Comment(20)
Seems like you figured it out in the second sentence of your question.Mender
@Mender I really do not understand why invoke or observe would not be useful, but ok...Bronwyn
I honestly think if is much easier to read and follow.Pectin
Your transform version uses 4 lines of code. My if version uses 2.Pectin
@Pectin I don’t, because with if the compiler doesn’t check that you’re not accidentally using the value in the else branch. In other words, it’s utterly pointless. It would be less pointless if the language provided assistance here; e.g. if the compiler complained if we used x in the else branch in this code: if(auto& x = *opt; opt) … else …; But unfortunately the language isn’t designed that way.Material
The and_then member lets you run a function but doesn't modify the optional. If you ignore the return value of and_then doesn't it do what you want? en.cppreference.com/w/cpp/utility/optional/and_thenElegiac
@JerryJeremiah doesnt that need to return std::optional? The return type (see below) must be a specialization of std::optional. Otherwise, the program is ill-formed.Bronwyn
open-std.org/jtc1/sc22/wg21/docs/papers/2021/… has an example of using and_then and directly comparing it to an equivalent if - if you ignore the return code of stoi in that example it does what you are after, right?Elegiac
@JerryJeremiah "and a function like std::stoi which returns a std::optional<int> instead of throwing on failure." So I think I still can not have function that returns voidBronwyn
No, you are right. It worked in my imagination. All and_then does for you is to not modify the optional - which wasn't a problem for you because you were returning the same thing you received. You still need to return something. Sorry for not testing my throry before suggesting it.Elegiac
"... since that will prevent implicit moves" wait implicit moves from what?Mender
Can you provide the construct in another (functional) programming language for comparison?Atonement
@Barry, nevermind, I was wrong I assumed it will suffer from the same problem accumulate had, where there would be unnecessary copy. now it is weird that it does not, but that is another questionBronwyn
@Mender mind :) , please see godbolt.org/z/bs3fxqh5zBronwyn
void returning transform was considered and rejectedTurino
Here is the vote result github.com/cplusplus/papers/issues/112#issuecomment-459286258 this topic could be extended again in C++26Atonement
@Bronwyn You don't need to move there either. That can just be return 0;Mender
@Mender true, but I want to keep the optional intact for further chaining... for example, I log when I get something, but then later that same thing is getting processed...Bronwyn
@Atonement great find, thank you, I really wish maybe_invoke existed.Bronwyn
@Turino or @ Sebastian I think both of your comments could be an answer.Bronwyn
C
8

The operations that were added to optional in C++23 are monadic in nature. That was the design of them. They fulfill certain common usage patterns that would otherwise be verbose and cumbersome to use. transform is for conditional transformations. and_then is for conditionally processing a whole new optional from an existing one. or_else is for cases where processing the lack of a value is pretty simplistic. And all of them work together to allow chaining.

But the basic act of "do something if the optional has a value" doesn't fit into this paradigm. It's not chainable. It's not monadic manipulation of the value. It's just normal use of an optional.

And doing it with if will not only be clearer as to what's going on, it'll have less noise (no lamdba and parameters, for example). There's just no reason to do that when the usual mechanism is just fine.

Churchman answered 8/2, 2022 at 3:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.