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

时间:2020-02-27 13:40:17

标签: c++ reflection c++20 c++-concepts

以下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;
};

1 个答案:

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