Подстановочные знаки для концепций C ++, говорящие «принимать что-либо для этого аргумента шаблона» - PullRequest
2 голосов
/ 25 февраля 2020

Есть ли способ разрешить concept с аргументами шаблона , чтобы быть в порядке с любым предоставленным параметром шаблона?

Т.е. какой-то мага подстановки c для заполнителя аргумента шаблона?

Пример использования:

template<class Me, TestAgainst>
concept derived_from_or_same_as = 
    std::same_as<Me, TestAgainst> ||
    std::derived_from<decltype(p.first), First>;

Выше необходимо, потому что, к сожалению, примитивные типы ведут себя иначе чем типы классов для is_base_of и derived_from.

Теперь мы можем определить Pair concept, который проверяет предоставленные типы:

template<class P, class First, class Second>
concept Pair = requires(P p) {
    requires derived_from_or_same_as<decltype(p.first), First>;
    requires derived_from_or_same_as<decltype(p.second), Second>;
};

Вариант использования [a] - принять любую действительную пару Как или подтип Как :

// this works well
void doWithPairOfA(const Pair<A, A> auto& p) { /* */ }

Вариант использования [b] - принять любую действительную пару, никаких ограничений на внутренние типы:

// this is *pseudo code* as Pair<auto, auto> is not allowed
void doWithAnyPair(const Pair<auto, auto> auto& p) { /* */ }

К сожалению, auto не допускается в качестве заполнителя аргумента шаблона в C ++ 20 .

Так что Pair<auto, auto> не является решение на данный момент.


Другие языки допускают такой синтаксис в некотором роде, хотя и не с такой же точной семантикой и значением, как здесь, но использование выглядит очень похоже.

Python:

// Any as wildcard
foo(lst: List[Any]) -> List[str]

Java:

// ? as wildcard
List<String> foo(List<?> lst)

Синтаксис до C ++ 20 выглядел бы как-то например 1 :

Вариант использования [a] - при попытке принять любую действительную пару As или подтип Как :

// not as good as with concepts above, this allows only "pair" of A and A
// **but rejects sub-types of A, which is not good**
// and there is no check that this is actually a pair (can be added with SFINAE)
template<template<class, class> typename PAIR>
void doWithPairOfA(const PAIR<A, A>& p) { /* */ }

Вариант использования [b] - принять любую допустимую пару, без r ограничения на внутренние типы:

// not as good as we would wish - we do allow any kind of "pair"
// but there is no check that this is actually a pair (can be added with SFINAE)
template<template<class, class> typename PAIR, typename ANY1, typename ANY2>
void doWithAnyPair(const PAIR<ANY1, ANY2>& p) { /* */ }

Pre C ++ 20 код

Могут ли концепции представить лучшее решение?


1 Смежный вопрос (до C ++ 20, для шаблонов, а не для концепций): Шаблоны, принимающие "что-нибудь" в C ++

1 Ответ

3 голосов
/ 25 февраля 2020

Вы можете получить шаблонное поведение , изменив Pair concept, чтобы принимать и проверять тип тега Любой .

Давайте сначала объявим Любой как класс тегов, не нужно его реализовывать.

class Any;

Теперь мы можем создать type_matches concept, чтобы проверить, соответствует ли тип T данному типу A , со следующими правилами:

T соответствует A

  • , если A равно Любое - или -
  • , если T == A или, если T получено из A

Как отмечено в вопросе, проверка для T == A или T является производным от A может быть сделано для типов классов только с помощью std::derived_from однако примитивные типы требуют добавления теста для std::same_as.

Сопоставление будет выполнено с помощью следующего кода:

template<class Me, class TestAgainst>
concept type_matches =
    std::same_as<TestAgainst, Any> ||
    std::same_as<Me, TestAgainst>  ||
    std::derived_from<Me, TestAgainst>;

Концепция Pair будет изменена на:

template<class P, class First, class Second>
concept Pair = requires(P p) {
    requires type_matches<decltype(p.first), First>;
    requires type_matches<decltype(p.second), Second>;
};

Код теперь может разрешить оба обязательных варианта использования.

Вариант использования [a] - принять любую действительную пару как или суб тип As :

// can be called with a Pair of As or sub-type of As
void doWithPairOfA(const Pair<A, A> auto& p) { /* */ }

Вариант использования [b] - принять любую допустимую пару, без ограничений для внутренних типов:

void doWithAnyPair(const Pair<Any, Any> auto& p) { /* */ }

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

...