Использование концепций для проверки, имеет ли тип T поле F - PullRequest
3 голосов
/ 27 февраля 2020

Следующая concept проверяет, имеет ли тип T общедоступное c поле foo :

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

Есть ли способ реализовать generi c concept, который проверяет, имеет ли тип T публичное c поле F , что-то вроде (псевдокод ... поле F нельзя передать так):

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

1 Ответ

8 голосов
/ 27 февраля 2020

Проверка наличия в указанном параметре поля F может быть легко достигнута с ограничением requires на саму функцию:

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

Почему (и когда) C ++ 20 требует requires requires см .: Почему мы требуем требует требует?

Выше метод может прекрасно жить вместе с generi c регистр перегрузки:

// the unconstrained version
void doSomething(auto i) { /* */ }

правильный метод будет выбран в соответствии с предоставленным параметром.

код: https://godbolt.org/z/u35Jo3


Чтобы иметь общую концепцию c, мы можем добавить макрос, чтобы помочь нам:

#define CREATE_HAS_FIELD_CONCEPT(field)     \
    template<typename T>                    \
    concept has_field_##field = requires {  \
        T::field;                           \
    }

У нас на самом деле нет общей концепции c, но мы можем легко сгенерировать требуемая концепция с указанным выше макросом:

CREATE_HAS_FIELD_CONCEPT(foo); // creates the concept: has_field_foo

И использовать его ( вместо версии с требует выше):

void doSomething(has_field_foo auto i) { /* */ }

Код: https://godbolt.org/z/R9nQ7Q


Существует определенная ценность в создании концепции, поскольку она может участвовать в частичном упорядочении.

С помощью В обычном ограничении мы не получаем частичного упорядочения, поскольку ограничения atomi c не считаются эквивалентными, но понятия atom c.

Таким образом, следующий код, основанный на простом ограничении, завершается неудачно с неопределенностью:

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