有了 C++20 概念,我开始使用它们作为定义模板类“接口”的一种方式,因为它们会导致非常好的编译器错误。然而,我在以符合人体工程学且可读的方式定义它们方面遇到了一些困难。
例如,假设我想创建一个概念
IFoo
,静态检查一个类是否具有以下三个具有确切输入/返回类型的函数:
struct Foo {
void bar(uint32_t a, uint16_t b, uint8_t c) {}
int32_t baz(int32_t d, int16_t e, uint8_t f) {return 0;}
void bax(char g, uint8_t *h, uint32_t *i) {}
};
如果这个概念定义正确,那么
static_assert(IFoo<Foo>);
就能编译。我可以在“单一”概念定义中获得这种行为的唯一方法如下:
template<typename T>
concept IFoo = requires(T ifoo, uint32_t a, uint16_t b, uint8_t c, int32_t d, int16_t e, uint8_t f, char g, uint8_t *h, uint32_t *i) {
{ifoo.bar(a, b, c)} -> std::same_as<void>;
{ifoo.baz(d, e, f)} -> std::same_as<int32_t>;
{ifoo.bax(g, h, i)} -> std::same_as<void>;
};
这满足了要求,但是它不是特别可读,因为你必须在心里将(许多)类型从
requires()
参数转移到函数输入,以形成一个想法,如果你实现这个类,函数原型应该是什么样子。实现此目的的另一种方法是为每个函数定义不同的概念并像这样链接它们:
template<typename T>
concept IFoo_bar = requires(T ifoo, uint32_t a, uint16_t b, uint8_t c) {
{ifoo.bar(a, b, c)} -> std::same_as<void>;
};
template<typename T>
concept IFoo_baz = requires(T ifoo, int32_t d, int16_t e, uint8_t f) {
{ifoo.baz(d, e, f)} -> std::same_as<int32_t>;
};
template<typename T>
concept IFoo_bax = requires(T ifoo, char g, uint8_t *h, uint32_t *i) {
{ifoo.bax(g, h, i)} -> std::same_as<void>;
};
template<typename T>
concept IFoo = requires {
requires IFoo_bar<T>;
requires IFoo_baz<T>;
requires IFoo_bax<T>;
};
然而,这会创建大量不理想的样板代码。我真正想要的(无法编译)是能够像这样嵌套需求块:
template<typename T>
concept IFoo = requires(T ifoo) {
requires(uint32_t a, uint16_t b, uint8_t c) {
{ifoo.bar(a, b, c)} -> std::same_as<void>;
};
requires(int32_t d, int16_t e, uint8_t f) {
{ifoo.baz(d, e, f)} -> std::same_as<int32_t>;
};
requires(char g, uint8_t *h, uint32_t *i) {
{ifoo.bax(g, h, i)} -> std::same_as<void>;
};
};
我错过了什么吗?或者我已经探索过的途径是实现我所追求的目标的唯一途径吗?我真的希望我在文档中遗漏了一些东西,这将使这变得更容易!
你可以用
&&
: 将它们链接起来
template<typename T>
concept IFoo =
requires(T ifoo, uint32_t a, uint16_t b, uint8_t c) {
{ifoo.bar(a, b, c)} -> std::same_as<void>;
} &&
requires(T ifoo, int32_t d, int16_t e, uint8_t f) {
{ifoo.baz(d, e, f)} -> std::same_as<int32_t>;
} &&
requires(T ifoo, char g, uint8_t *h, uint32_t *i) {
{ifoo.bax(g, h, i)} -> std::same_as<void>;
};