使用概念检查类型 T 是否具有字段 F

Using concepts for checking if a type T has a field F

下面的concept检查类型T是否有public字段foo:

template<typename T>               
concept has_field_foo = requires { 
    T::foo;                       
};

有没有一种方法可以实现通用 concept 来检查类型 T 是否具有 public 字段 F,类似(伪代码...字段F不能这样传递):

template<typename T, typename F>               
concept has_field = requires { 
    T::F;
};

检查提供的参数是否具有字段 F 可以通过对函数本身的 requires 约束轻松实现:

// accepts only parameters that have a 'foo' member
void doSomething(auto i) requires requires { i.foo; } { /* */ }

For why (and when) C++20 requires requires requires see:

以上方法可以与泛型 case 重载完美共存:

// 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

并使用它(代替上面需要的版本:

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!
}