如何最好地在 if 语句中测试和解包 std::optional

Ale*_*lex 5 c++ c++17 stdoptional

我有多个函数返回一个std::optional<T>. 下面是一个虚构类型的例子MyType

struct MyType {
    // ...
}

std::optional<MyType> calculateOptional() {
    // ... lengthy calculation

    if (success) {
        return MyType(/* etc */);
    }

    return std::nullopt;
}
Run Code Online (Sandbox Code Playgroud)

让我们假设这些函数的运行成本很高,我想避免多次调用它们。

调用它们时,我想立即测试可选项,如果它确实包含一个值,我想立即使用它,永远不要再使用它。例如,在 Swift 中,我可以使用标准if-let语句:

if let result = calculateOptional() {
    // Use result var
}
Run Code Online (Sandbox Code Playgroud)

我想在 C++ 中复制这种测试和解包行为,同时在使用时尽可能保持代码干净。例如,显而易见的简单解决方案(至少对我而言)是:

if (auto result = calculateOptional()) {
    MyType result_unwrapped = *result;
    // Use result_unwrapped var
}
Run Code Online (Sandbox Code Playgroud)

但是您必须在 . 中解包if,或者*result在任何地方使用,这与 Swift 无关。

到目前为止,我唯一真正接近 Swift 的外观和感觉的解决方案是:

template<typename T> bool optionalTestUnwrap(std::optional<T> opt, T& value) {
    if (!opt.has_value()) { return false; }
    value = *opt;
    return true;
}

#define ifopt(var, opt) if (typename decltype((opt))::value_type (var); optionalTestUnwrap((opt), (var)))

ifopt (result, calculateOptional()) {
    // Use result var
}
Run Code Online (Sandbox Code Playgroud)

...但我也不喜欢使用宏来代替普通if语句。

Bar*_*rry 6

就个人而言,我只会这样做:

if (auto result = calculateOptional()) {
    // use *result
}
Run Code Online (Sandbox Code Playgroud)

其次是给可选的一个丑陋的名字并为它制作一个更好的别名:

if (auto resultOpt = calculateOptional()) {
    auto& result = *resultOpt;
    // use result
}
Run Code Online (Sandbox Code Playgroud)

我认为这已经足够了。这是有意遮蔽外部作用域名称(即命名 theoptional和内部 alias result)的一个很好的用例,但我认为我们不需要在这里发疯。即使使用*result也不是大问题——类型系统可能会捕获所有误用。


如果我们真的想使用 Swift,您正在使用的宏需要默认构造 - 而这并不是真正必要的。我们可以做得更好一点(理想情况__opt下被一种选择唯一名称的机制代替,例如与 连接__LINE__):

#define if_let(name, expr)              \
    if (auto __opt = expr)              \
        if (auto& name = *__opt; false) {} else
Run Code Online (Sandbox Code Playgroud)

如:

if_let(result, calculateOptional()) {
    // use result
} else {
    // we didn't get a result
}
Run Code Online (Sandbox Code Playgroud)

这没有任何额外的开销或要求。但这有点荒谬,有其自身的问题,而且似乎不值得。但如果我们只是玩玩,这很管用。