用于返回带有布尔结果标志的值的标准模板

时间:2018-11-13 14:44:46

标签: c++ c++17 c++-standard-library

由于我开始利用C ++ 17结构化绑定,并且如果运算符init语句用于更优美的功能结果报告和检查,那么我开始进行以下操作(如果遵循C ++核心准则F21:

std::pair<bool, int>Foo()
{
    return {true, 42}; //true means that function complete with no error and that 42 is a good value
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

然后,当然,我虽然很高兴为此类返回类型提供一个可重用的模板,以便没有人需要复制该对中的bool部分:

template <typename T> using validated = std::pair<bool,T>;

validated<int> Foo()
{
    return {true, 42};
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

这对我来说很棒,但是现在我想知道是否有某种等同于此模板的标准,这样我就不必重新发明轮子并自己定义它了。似乎一个带有有效性标志的任意类型值将是一个有用的构造,但是我在标准库中找不到任何内容。我想念什么吗?

2 个答案:

答案 0 :(得分:9)

std::optional正是您要问的。甚至在描述中:

  

可选的一个常见用例是可能失败的函数的返回值。与std::pair<T,bool>之类的其他方法相反,可选方法可以很好地处理昂贵的构造对象,并且由于意图明确表达,因此可读性更高。

该示例中的if看起来会更简单:

#include <optional>
#include <iostream>

std::optional<int> Foo(bool fail)
{
    if (!fail) return {42};
    return {};
}

void process(bool fail) {
    if (auto val = Foo(fail)) {
        std::cout << val.value() << '\n';
    } else {
        std::cout << "No value!\n";
    }    
}

int main() {
    std::optional<int> oi;
    process(true);
    process(false);
}

如果您确实希望显式使用Value,则可以始终通过成功分支上的引用来解压缩它,即auto Value = val.value();

您需要注意一些警告。 2从我的头顶开始:

  1. 性能:Why is the construction of std::optional<int> more expensive than a std::pair<int, bool>?,尽管对于给定的示例up-to-date clang with -O3 looks pretty convicing
      

    注意:为简洁起见,为static添加了process-为防止生成用于外部链接的版本。

  2. 如果默认构造了对象,它将返回false。可能会感到有些惊讶,optional的默认构造不是默认构造基础价值。

编辑: 在评论之后,我决定明确声明没有类似于pair<T,bool> 的类型别名或与标准库兼容的类似名称。要证明不存在某些东西并不容易,但是如果存在这样的类型,标准库肯定会在insert的声明中使用它,但事实并非如此。因此,我强烈暗示它周围没有任何语义包装器。

答案 1 :(得分:1)

您可能对拟议的std::expected感兴趣。

其界面非常接近std::optional。的主要优势 expected<T, E>胜过optional<T>的一种功能是传送错误:

enum class errc {err1, err2, err3};

std::expected<int, errc> Foo()
{
  if (/* error condition 1 */)  return std::unexpected(errc::err1);

  // ... checking other error conditions

  return 42;  // no error condition (42 is a good value)
              // implicit conversion from `int` to `expected<int, errc>`
              // avoid boilerplate code
}

int main()
{
  auto q = Foo();

  if (q)
  {
    // Do something with the return value here
  }
}

您也可以看看:


作为旁注main() must return int