首页 > 解决方案 > 如何设计一个“Awaitable”基类?

问题描述

C++ 标准库在很多地方都提供了一种“可等待”的 API:例如std::futurestd::condition_variable可以立即“尝试”获取它们的值,无限期地“等待”它们的值,“wait_for”某个std::chrono::duration,或“wait_until”某个std::chrono::time_point到达. 我正在努力创建一个捕获这些相同操作的抽象基类。

template <typename T>
class awaitable {
 public:
  virtual std::optional<T> try() = 0;

  virtual std::optional<T> wait() = 0;

  template <typename Rep, typename Period>
  virtual std::optional<T> wait_for(const std::chrono::duration<Rep, Period>&) = 0;

  template <typename Clock, typename Duration>
  virtual std::optional<T> wait_until(const std::chrono::time_point<Clock, Duration>&) = 0;
};

try并且wait没有问题。wait_for并且wait_until需要模板参数,因此不能是虚拟的。

有没有一种“干净”的方式来定义这样的接口?`

我考虑过的一些选项(除非我遗漏了什么)似乎不可行:

到目前为止,第三种变体似乎是我唯一的选择,而不是一个很好的选择。

标签: c++templatesc++17virtual-functionsmultidispatch

解决方案


我认为你的第三种方式可能是要走的路。有疑问,您必须付出一些努力来限制可能的误用。

您还可以查看非循环访问者模式:

https://en.wikipedia.org/wiki/Visitor_pattern

非循环访问者 C++

对于某些场景,Andrei Alexandrescu 提供的实现方式(真的可以推荐他的书)对我帮助很大。它需要一些努力才能完全理解,有点侵入性,据我所知,至少对于 C++ < 14,不可能在这里保持 100% 的宏免费,但它具有你可能需要的去中心化的巨大优势。此外,对于大多数用例和现代架构来说,它的次要 dynamic_cast-usage(不被误用作动态开关)并不是真正的问题。


推荐阅读