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

时间:2017-07-04 18:28:55

标签: c++ templates generic-programming type-constraints

当使用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的类时,验证的协议一致性会有所帮助。 (当然,没有人阻止我编写ProtocolT用于文档目的,但编译器不会验证ProtocolT至少包含所有必需的方法。)

2 个答案:

答案 0 :(得分:4)

您要查找的功能称为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();
}

答案 1 :(得分:2)

可能会插入相应的static_assert

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

另请注意,在您的示例中,当T符合ProtocolT要求时,它仍然无效,因为f接受const对T的引用,而ProtocolT仅表示它应该有非const g()