具有自定义返回类型的函数和" false"返回条件?

时间:2018-01-05 08:11:04

标签: c++

我有一个返回自定义类结构的函数,但是如何处理我希望通知用户函数失败的情况,如返回Cell CSV::Find(std::string segment) { Cell result; // Search code here. return result; }

我的功能看起来像这样:

Cell.data

因此,当成功时,它会返回正确的结果,但是当它失败时我该如何处理?

我考虑在Cell中添加一个布尔方法来检查Cell.IsEmpty()是空的(onSuspendCanceled)。但我是否以过于复杂的方式思考这个问题?

6 个答案:

答案 0 :(得分:41)

有三种一般方法:

  • 使用例外。这就是Bathsheba的答案。
  • 返回std::optional<Cell>(或其他可能包含或不包含实际Cell的类型。)
  • 返回bool,并添加Cell &参数。

其中哪一个最好取决于您打算如何使用此功能。如果主要用例传递有效的段,那么一定要使用例外。

如果此功能的部分设计是可用于判断某个段是否有效,则异常不合适,我首选的选择是std::optional<Cell>。这可能不适用于您的标准库实现(这是C ++ 17的功能);如果没有,boost::optional<Cell>可能有用(如Richard Hodges的回答中所述)。

在评论中,代替std::optional<Cell>,用户您建议使用expected<Cell, error>(不是标准C ++,但建议用于未来的标准,并且可以在std命名空间之外实现)。如果有多种可能的原因,这可能是一个很好的选择,可以添加一些指示,说明为什么没有找到Cell传入的segment参数。

第三个选项我主要包括完整性。我不推荐它。它是其他语言中流行且通常很好的模式。

答案 1 :(得分:7)

这个函数是一个查询,它可以有效地找不到单元格,还是一个必要条件,预计会找到单元格?

如果是前者,则返回一个可选的(或可以为空的指针)单元格。

如果是后者,如果找不到则抛出异常。

前任:

boost::optional<Cell> CSV::Find(std::string segment) {
  boost::optional<Cell> result;
  // Search code here.
  return result;
}

后期: 就像你拥有它一样。

当然还有基于c ++ 17变体的方法:

#include <variant>
#include <string>

struct CellNotFound {};
struct Cell {};

using CellFindResult = std::variant<CellNotFound, Cell>;


CellFindResult Find(std::string segment) {
  CellFindResult result { CellNotFound {} };

  // Search code here.
  return result;
}

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

void cellsAndStuff()
{
    std::visit(overloaded
    {
        [&](CellNotFound)
        {
            // the not-found code
        },
        [&](Cell c)
        {
            // code on cell found
        }
    }, Find("foo"));
}

答案 2 :(得分:4)

处理严重失败的C ++方法是定义一个形式的异常类:

struct CSVException : std::exception{};

在你的函数中,然后throw失败分支中的一个:

Cell CSV::Find(std::string segment) {
  Cell result;
  // Search code here.
  if (fail) throw CSVException();
  return result;
}

然后,您在主叫站点使用try catch块处理失败案例。

然而,如果&#34;失败&#34;分支是正常行为(主观确实,但只有你可以判断正常性),然后确实在Cell内填充某种失败指标,或者甚至可能将返回类型改为std::optional<Cell>

答案 3 :(得分:0)

如果您可以使用C ++ 17,另一种方法是使用std::optional类型作为返回值。这是一个可能包含或不包含值的包装器。然后,调用者可以检查您的函数是否实际返回了一个值,并处理它没有的情况。

std::optional<Cell> CSV::Find(std::string segment) {
  Cell result;
  // Search code here.
  return result;
}

void clientCode() {
   auto cell = CSV::Find("foo");
   if (cell)
      // do stuff when found
   else
      // handle not found
}

答案 4 :(得分:0)

另一个选项是使用多个返回值:

std::pair<Cell, bool> CSV::Find(std::string segment) {
  Cell result;
  // Search code here.
  return {result, found};
}

// ...

auto cell = CSV::Find("foo");
if (cell->second)
  // do stuff with cell->first

布尔标志表示是否找到了请求的Cell

投票站
  • 众所周知的方法(例如std::map::insert);
  • 非常直接:值和成功指标是函数的返回值。
CON外
  • firstsecond的模糊性要求始终记住对中值的相对位置(C ++ 17 structured bindings部分解决了此问题);
  • 可能会失去安全性(调用代码必须在使用之前检查是否有结果值)。

详细信息

答案 5 :(得分:0)

对于解析,通常最好避免std::string而是使用std::string_view;如果C ++ 17不可用,那么最小功能的版本就可以轻松实现。

此外,不仅要跟踪解析的内容,还要追踪余下的

跟踪剩余部分有两种可能性:

  • 采取可变参数(通过引用),
  • 归还剩余部分。

我个人更喜欢后者,因为在出​​现错误的情况下,它可以保证调用者手中有一个未修改的值,这对错误报告非常有用。

然后,您需要检查可能发生的错误,以及您希望的恢复机制。这将告知设计。

例如,如果您希望能够解析格式错误的CSV文档,那么Cell能够表示格式错误的CSV单元是合理的,在这种情况下,界面非常简单:< / p>

std::pair<Cell, std::string_view> consume_cell(std::string_view input) noexcept;

函数始终前进且Cell 可能包含正确的单元格或格式不正确的单元格。

另一方面,如果您只希望支持格式良好的CSV文档,那么通过异常发出错误信号并且Cell只能保存实际单元格是合理的:

std::pair<std::optional<Cell>, std::string_view> consume_cell(...);

最后,您需要考虑如何发出行结束条件的信号。它可能是Cell上的简单标记,但此时我个人更喜欢创建迭代器,因为它提供了更自然的界面,因为行是Cell的范围。

迭代器的C ++接口有点笨重(因为你需要一个“结束”,并且在解析之前结束是未知的),但是我建议坚持使用它以便能够使用带有for循环的迭代器。但是,如果您希望偏离它,至少可以使用while轻松使用它,例如std::optional<Cell> cell; while ((cell = row.next())) { ... }