如何使用 C++ 模板执行正式协议?

How to enforce a formal protocol with C++ templates?

使用 template style 固有的编译时鸭子类型时,是否有任何方法可以强制要求模板参数实现具有特定签名的特定方法?

struct ProtocolT {
  void g() const;
  void h();
}

// I want the compiler to check that T conforms to ProtocolT
// that is, T must implement g() and h() rather than just g()
template <typename T>
void f(const T& x) {
  x.g();
}

当然,即使没有这个,也有完美的类型安全:如果模板参数T没有在模板函数实现中使用的方法,编译器总是会报错。

但我觉得明确说明 class T 必须具有某些 class ProtocolT 中指定的所有方法很有吸引力。它允许我在开发过程的早期通过要求 T 中我尚未在模板函数实现中使用的方法来约束设计。

即使我没有在 ProtocolT 中包含任何未使用的方法,我仍然认为当我需要编写可用作 T 的 class 时,经过验证的协议一致性会有所帮助。 (当然,没有人阻止我出于文档目的编写 ProtocolT,但是编译器不会验证 ProtocolT 至少包含所有必需的方法。)

也许插入对应的static_assert:

static_assert
(
    ::std::is_same< void, decltype(::std::declval< T >().h()) >::value
,   "T must implement void h(void)"
);

另请注意,在您的示例中,当 T 遵循 ProtocolT 要求时,它仍然无法工作,因为 f 接受对 T 的 const 引用,而 ProtocolT 仅说它应该有非常量 g().

您正在查找的功能称为 concepts。它们目前是技术规范; GCC 有精简版概念的实现。

使用概念看起来像(我不太熟悉语法,所以可能会略有不同):

template <typename T>
concept bool Protocol = requires(const T a, T b) {
  { a.g() } -> void;
  { b.h() } -> void;
};

void f(const Protocol& x) {
  x.g();
}

但是,如果您想要解决方案,可以立即使用,您可以使用多种技术模拟概念。

您可以编写类型特征来检测某个函数是否满足您的要求。

您也可以使用 detection idiom,它抽象了以前的技术,大大减少了样板文件。对于您的示例:

template <typename T>
using g_t = decltype(std::declval<const T&>().g());

template <typename T>
using h_t = decltype(std::declval<T&>().h());

template <typename T>
constexpr bool meets_protocol_v = std::experimental::is_detected_exact_v<void, g_t, T>
            && std::experimental::is_detected_exact_v<void, h_t, T>;

在使用它时,您可以是 SFINAE 友好型和 SFINAE 关闭 meets_protocol_v,或者您可以静态断言:

template <typename T>
void f(const T& x) {
  static_assert(meets_protocol_v<T>, "Doesn't meet protocol");
  x.g();
}