【问题标题】:Generic circular buffer implementation in C++C++ 中的通用循环缓冲区实现
【发布时间】:2017-06-11 09:52:34
【问题描述】:

我开始编写我的循环缓冲区库,只是为了好玩。我遇到了一个特殊的问题:

template < class T >
T CircularBuffer<T>::pop()

它返回类型T,但是当CB为空的情况下,用户尝试使用pop()呢?返回 nullptr 没有意义,因为 nullptr 不能转换为例如 double。显然可以抛出异常,但是有没有更语义友好的方法呢?

编辑:“语义友好”是指:

当推入一个完整的 CB 时,抛出异常是很自然的。异常应该防止程序崩溃,因为新元素没有足够的内存。从语义上讲,在弹出空 CB 时抛出异常对我来说似乎很不合适,因为程序不会崩溃。但如果这是唯一可用的选项,请告诉我。

【问题讨论】:

  • 抛出异常在语义上没有什么不友好的地方。但也许尝试将pop 分为poptop。因此它可能会变成一个 noop,而 top 实际上会抛出。它还消除了删除时复制的需要,这总是很好。
  • “很明显,可以抛出异常,但是有没有更语义友好的方法呢?” 你是什么意思语义友好 .抛出异常可能是唯一有效的语义。你也应该看看std::queue 是如何解决这个问题的。 pop() 函数返回void 并且通过front() 访问最顶层的元素。
  • 如果您使用支持最新 C++2017 标准库的编译器,这是 std::optional 的用途之一。
  • 返回码解决方案:Status pop(T&amp; out); 其中Status是一种向调用者报告调用状态的类型:错误、成功、空、内存不足等。为方便起见,您可以将@ 987654333@ 可转换为 bool 并提供错误的纯文本描述。在任何情况下,无论您选择返回码、异常还是其他错误处理方式,都要在整个库代码中一致地实现它,不要混淆它们。

标签: c++ circular-buffer


【解决方案1】:

这是std::optionalboost::optional 的完美用例——它们是存储值为空状态的类。

您可以将签名更改为:

template < class T >
std::optional<T> CircularBuffer<T>::pop()

您的合同将大致如下:

  • 如果缓冲区为空,则返回std::nullopt

  • 否则,将返回包含头元素的非空std::optional&lt;T&gt;


或者,考虑采用一个“延续”函数,该函数只有在弹出成功时才会被调用。示例:

template < class F >
decltype(auto) CircularBuffer<T>::pop(F&& continuation);

用法:

some_circular_buffer.pop([](auto popped_item)
{
    // I will only be called if the pop was successful.
});

您还可以通过对“弹出失败”案例进行额外的延续来扩展此概念。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多