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?
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?
#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;}});
}
operator decltype(auto)() const
is nicer IMO (less parens). :) –
Greene operator invoke_result_t<F>()
–
Fragment 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.
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. –
Shelbyshelden value_or_else
. –
Fragment C++23 adds a closely related feature: https://en.cppreference.com/w/cpp/utility/optional/or_else
std::optional<int> f();
std::optional<int> g();
std::optional<int> y = f().or_else(g);
Note however that the or_else()
argument itself returns std::optional<T>
rather than T
.
So 3 options, all a little bit clunkier than envisioned:
f().or_else(g).value()
if available and std::optional<T>
return type for the or_else
argument isn't a bother.lazy_value_or(f(), g)
, sacrificing the monadic method chain syntax.auto opt = f(); return opt? std::move(*opt) : g();
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;}) ();
© 2022 - 2024 — McMap. All rights reserved.
template <typename F> Lazy(F f) -> Lazy<F>;
? It looks like a forward declaration of a function template with trailing return type, but it isn't... – Jadotville