当使用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
至少包含所有必需的方法。)
答案 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()
。