2

I want to initialise an std::optional from another std::optional and some additional arguments provided that the latter std::optional is not empty. Unfortunately std::optional::optional 4) and 5) are not suitable as the amount of arguments is different.

I was able to come up with the following, but it still feels excessive. I particularly don't like specifying return type for the lambda explicitly.

Is there an better (as in more succinct and more expressive) way to achieve this?

#include <iostream> #include <optional> #include <tuple> struct A { A(std::optional<int> oi, float f, char c) : val{ [&] () -> decltype(val) /* I don't like specifying type here */ { if (oi) return {{*oi, f, c}}; else return std::nullopt; }() } { } std::optional<std::tuple<int, float, char>> val; }; int main() { auto print = [](auto& r) { if (r) std::cout << std::get<0>(*r) << "; " << std::get<1>(*r) << "; " << std::get<2>(*r) << std::endl; else std::cout << "nullopt" << std::endl; }; auto one = A({}, 1.0, 'c'); print(one.val); auto two = A(10, 2.0, 'c'); print(two.val); } 

Live example.

7
  • 1
    val{ oi ? decltype(val){{*oi, f, c}} : std::nullopt }? Commented Oct 26, 2017 at 1:35
  • @zneak yep, it will work, but can we get rid of decltype by any chance? Commented Oct 26, 2017 at 1:37
  • Typedef it? I think that your hands are tied by how type inference works here. Commented Oct 26, 2017 at 1:40
  • The "real idiomatic" way to deal with this would be to have a flat_map function to go with std::optional, but I don't think that there's a standard one. Commented Oct 26, 2017 at 1:41
  • std::tuple{*oi, f, c} is probably clearer than the decltype, though they're not identical. Commented Oct 26, 2017 at 1:42

1 Answer 1

4

Just trust the compiler to do the right thing:

A(std::optional<int> oi, float f, char c) { if (oi) { val.emplace(*oi, f, c); } } 

This is also of course a great use-case for a map() member function, which you can write as a non-member function:

A(std::optional<int> oi, float f, char c) : val(map(oi, [&](int i){ return std::tuple(i, f, c); })) { } 

Recently proposed in P0798 by TartanLlama.

Sign up to request clarification or add additional context in comments.

6 Comments

So no chance to avoid default-construction of val in the first example before map() is in the standard?
@DevNull Is that bad? Default-constructing an optional is just setting a bool. The compiler will optimize it properly, my example and your example generate identical code.
no, it's not "bad", I just hoped that there's some way to both leave it in the member initialiser list and avoid specifying type explicitly.
@DevNull The map solution above does exactly that? (It is the map function from Haskell, sometimes called fmap)
@Yakk yes, but it's not in the standard (maybe yet).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.