std::optional::value_or() - 惰性参数求值

20

是否有可能以一种懒加载的方式评估std::optional::value_or(expr)参数,以便只在没有值的情况下计算expr?

如果不行,那么适当的替换是什么?

3个回答

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;}});
}

演示


4
template <typename F> Lazy(F f) -> Lazy<F>; 这是什么样的结构?它看起来像一个带有尾返回类型的函数模板的前向声明,但实际上不是... - Ben Voigt
9
@BenVoigt 这是一个模板类型推导指南。 - Piotr Skotnicki
3
在我看来,operator decltype(auto)() const 更好(少用括号)。 :) - Rakete1111
3
@Rakete1111 哦天啊,这让我脑子短路了......“嘿,这是一个关于......嗯......我们再看看吧!” - Quentin
2
@Rakete1111 还有 operator invoke_result_t<F>() - Barry

11
你可以编写你的辅助函数:
template<typename T, typename F>
T lazy_value_or(const std::optional<T> &opt, F fn) {
    if(opt) return opt.value();
    return fn();
}

这可以用作:

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

如果使用宏定义比显式编写更节省打字量,这由您来判断;但您可以使用宏定义使其变得更短:

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

用作

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

这是最接近我认为你想要的,但可能会被看作隐藏了太多东西而不受欢迎。

3
你可以使用一些宏来简化这个过程。请不要提倡使用宏。 - Jesper Juhl
16
@JesperJuhl 就像 goto 语句一样,宏是一种工具,有其适用的场合,即使在现代 C++ 中也是如此;将它们一概而论地妖魔化,就像任何 "绝对" 判断一样,是错误和短视的。这个情况有点边缘化,因为它隐藏了相当多的东西(我甚至提醒过这一点),但这取决于 OP 的判断。 - Matteo Italia
2
它们是工具箱中的一种工具,当然。它们有它们的用途。但是,我认为我们不应该鼓吹使用宏,因为大多数宏的使用是不好的或不合适的 - 大多数事情可以在没有使用宏的情况下更好地完成。所以,是的,它们存在。它们有其作用。只有在最后和唯一的选择时才提到它们(在我看来)。 - Jesper Juhl
1
作为一种命名的说明,一些语言(例如Rust)将其称为value_or_else - Barry

0
创建一个函数类型的可选项。
然后可以传入一个 lambda 表达式,当调用时会在请求的时刻计算出正确的值。
std::optional<std::function<int()>> opt;

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

int b = 4;

int c = opt.value_or([=] { return b * 10 + 2;}) ();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接