以下concept
检查类型 T 是否具有公共字段 foo :
template<typename T>
concept has_field_foo = requires {
T::foo;
};
是否有一种方法可以实现泛型concept
,该方法将检查类型 T 是否具有公共字段 F ,类似于(伪代码。 。字段 F 不能这样传递):
template<typename T, typename F>
concept has_field = requires {
T::F;
};
答案 0 :(得分:8)
通过对函数本身施加requires
约束,可以很容易地检查提供的参数是否具有字段 F :
// accepts only parameters that have a 'foo' member
void doSomething(auto i) requires requires { i.foo; } { /* */ }
关于C ++ 20为什么(和何时)需要
requires
requires
的信息,请参见: Why do we require requires requires?
上述方法可以完美地与一般的大小写重载一起使用:
// the unconstrained version
void doSomething(auto i) { /* */ }
根据提供的参数选择正确的方法。
代码:https://godbolt.org/z/u35Jo3
要有一个通用的概念,我们可以添加一个宏来帮助我们:
#define CREATE_HAS_FIELD_CONCEPT(field) \
template<typename T> \
concept has_field_##field = requires { \
T::field; \
}
我们实际上没有通用的概念,但是我们可以通过上面的宏轻松生成所需的概念:
CREATE_HAS_FIELD_CONCEPT(foo); // creates the concept: has_field_foo
并使用它(代替上面带有 requires 的版本的 ):
void doSomething(has_field_foo auto i) { /* */ }
代码:https://godbolt.org/z/R9nQ7Q
实际上可以创建一个概念,因为它可以参与部分排序。
使用普通约束时,我们不会得到部分排序,因为原子约束不被认为是等效的,但是原子概念却是等效的。
因此,以下基于普通约束的代码因含糊而失败:
void print(auto i) requires requires { i.foo; } {
std::cout << "foo" << std::endl;
}
void print(auto i) requires requires { i.moo; } {
std::cout << "moo" << std::endl;
}
void print(auto i) requires requires { i.moo && i.foo; } {
std::cout << "foo and moo" << std::endl;
}
struct HasFoo { int foo; };
struct HasMoo { int moo; };
struct HasFooAndMoo: HasFoo, HasMoo {};
int main() {
print(HasFoo{});
print(HasMoo{});
print(HasFooAndMoo{}); // compilation error: ambiguity
// all 3 'print' functions are proper candidates
// no partial ordering for constraints, just for concepts!
}
此功能可以按需运行:
CREATE_HAS_FIELD_CONCEPT(foo); // creates the concept: has_field_foo
CREATE_HAS_FIELD_CONCEPT(moo); // creates the concept: has_field_moo
void print(has_field_foo auto i) {
std::cout << "foo" << std::endl;
}
void print(has_field_moo auto i) {
std::cout << "moo" << std::endl;
}
template<class P>
concept has_fields_foo_and_moo
= has_field_foo<P> && has_field_moo<P>;
void print(has_fields_foo_and_moo auto i) {
std::cout << "foo and moo" << std::endl;
}
int main() {
print(HasFoo{});
print(HasMoo{});
print(HasFooAndMoo{}); // partial ordering for concepts rocks!
}