这是 std::expected 的正确便利包装器吗?

Is this a correct convenience wrapper for std::expected?

提问人:dw218192 提问时间:10/17/2023 最后编辑:Jarod42dw218192 更新时间:10/17/2023 访问量:124

问:

如果我正在编写一个返回 std::expected 对象的函数,并可能调用返回对象的其他函数,我发现自己编写这样的代码片段非常常见。std::expected

struct Foo {  };
std::expected<Foo, std::string> f();

auto res = f();
if(!res) return std::unexpected { res.error() };
auto val = res.value();
// do something with val

因此,我编写了一个这样的宏,在成功时“返回”值,在失败时“返回”错误。

#define CHECK(expr)\
({\
auto res = expr;\
if(!res) return std::unexpected { res.error() };\
res.value();\
})

然后,我可以这样使用它:

Foo foo = CHECK(f());

我假设内部作用域中变量的生存期应该与赋值表达式一样长。这是正确的吗?有没有可能出错的情况?

C++ std std -预期

评论

4赞 Igor Tandetnik 10/17/2023
请注意,您的宏依赖于语句表达式,这是 GCC 编译器的非标准功能。它不适用于其他编译器。
2赞 Toby Speight 10/17/2023
如果您认为代码工作正常,请考虑在代码审查中以更完整的方式展示您的工作(如果可以的话,包括其单元测试)。您可能会收到一些建议,使其更高效、更易于阅读和更好地测试。在你这样做之前,一定要先阅读 A guide to Code Review for Stack Overflow users,因为有些事情的处理方式是不同的——例如,问题标题应该简单地说明代码的作用,因为问题总是,“我怎样才能改进它?

答:

6赞 Patrick Roberts 10/17/2023 #1

使用此宏,您可以编写如下函数:

std::expected<Qux, std::string> g() {
  Foo foo = CHECK(f());
  Bar bar = CHECK(b(foo));
  return q(bar);
}
  • 无法从此模式推断返回类型
  • 要了解此代码的控制流,需要知道(并记住)宏扩展的内容

我认为避免这种模式是一元方法 std::expected<T,E>::and_then 的用途:

auto g() {
  return f()
      .and_then([](auto foo) { return b(foo); })
      .and_then([](auto bar) { return q(bar); });
}

在这种特殊情况下,它可以进一步缩短:

auto g() {
  return f()
      .and_then(b)
      .and_then(q);
}

尽管实际上,我认为写出 lambda 将是实际代码中更常见的情况。

编译器资源管理器:https://godbolt.org/z/vovTYfxf4