20

Is it possible to evaluate std::optional::value_or(expr) argument in a lazy way, so the expr were calculated only in the case of having no value?

If not, what would be a proper replacement?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
vtrz
  • 591
  • 1
  • 6
  • 12

3 Answers3

25
#include <optional>

template <typename F>
struct Lazy
{
    F f;  

    operator decltype(f())() const
    {
        return f();
    }
};

template <typename F>
Lazy(F f) -> Lazy<F>;

int main()
{
    std::optional<int> o;

    int i = o.value_or(Lazy{[]{return 0;}});
}

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
11

You may write your helper function:

template<typename T, typename F>
T lazy_value_or(const std::optional<T> &opt, F fn) {
    if(opt) return opt.value();
    return fn();
}

which can then be used as:

T t = lazy_value_or(opt, [] { return expensive_computation();});

If that's significantly less typing than doing it explicitly that's up to you to judge; still, you can make it shorter with a macro:

#define LAZY_VALUE_OR(opt, expr) \
    lazy_value_or((opt), [&] { return (expr);})

to be used as

T t = LAZY_VALUE_OR(opt, expensive_calculation());

This is closest to what I think you want, but may be frowned upon as it hides a bit too much stuff.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • 3
    "you can ease this up with some macros" - please don't advocate the use of macros. – Jesper Juhl Aug 05 '18 at 15:38
  • 16
    @JesperJuhl just like `goto`, macros are a tool and have their place, even in modern C++; demonizing them tout court, as for any "absolute" judgment, is wrong and shortsighted. This case is a bit borderline, as it hides quite some stuff (and I even warned about this), but that's up to OP to judge. – Matteo Italia Aug 05 '18 at 15:47
  • 2
    They are a tool in the toolbox, sure. And they have their uses. But, I don't think we should go around advocating their use since *most* use of macros is bad or inappropriate - *most* things can be done better without the use of macros. So yes, they exist. They serve a purpose. Just don't mention them unless they are the last and *only* resort (IMHO). – Jesper Juhl Aug 05 '18 at 15:52
  • 1
    As a naming note, some languages (e.g. Rust) call this `value_or_else`. – Barry Aug 05 '18 at 21:09
0

Make an optional of function type.

Then a lambda can be passed in, which when called will calculate the correct value at the requested moment.

std::optional<std::function<int()>> opt;

int a = 42;
opt = [=] { return a; }

int b = 4;

int c = opt.value_or([=] { return b * 10 + 2;}) ();
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31