Указание концепции для типа, который имеет шаблон функции-члена, используя Concepts Lite

Я пытаюсь определить концепцию, чтобы ограничить тип с более высоким родом, который имеет шаблон функции-члена, используя Concepts Lite. Однако я не могу найти внутри техническая спецификация или руководство пункт, касающийся шаблонных утверждений внутри концепции.

Как это сделать?

Пример: предположим, у меня более высокий тип HKT с шаблоном функции-члена F:

template<class T>
struct HKT {
template<class U> // this looks like e.g. rebind in std::allocators
auto F(U) -> HKT<U>;
};

и что теперь я хочу указать концепцию для ограничения этих типов с более высоким родом:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) { // HKT<T> is a type, h is an object
// HKT<T> needs to have a member function template that
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)
template<class U>  // is there a syntax for this?
h.F(std::declval<U>()) -> HKT<U>;
}
}

Обратите внимание, что я мог бы сделать что-то вроде:

template <template <class> class HKT, class T, class U>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
h.F(std::declval<U>()) -> HKT<U>;
}
}

но это значит что мне нужно знать U на сайте ограничения.

Мне действительно все равно, если замена для данного U не удается или нет, хотя я понимаю, почему это может быть проблемой: например, примените ограничение, чтобы убедиться, что ваша функция не завершается с ошибкой, а затем завершается сбоем, потому что ограничение было выполнено, но во время создания экземпляра произошла ошибка в шаблоне функции-члена (поможет ли это, если шаблон функции-члена был ограничен?).

61

Решение

Давайте подумаем о требованиях, которые вы хотите от вашего комментария:

// HKT<T> needs to have a member function template that
// returns HTK<U> where the type U is to be deduced and
// it can be any type (it is unconstrained)

В то время как Concepts требует, чтобы мы основывали свои ограничения на конкретных типах, мы можем быть умными в выборе конкретных типов, которые мы используем. Что вы подразумеваете под U является любой тип. Действительно ли вообще какой-либо тип? Подумайте о минимально возможном наборе ограничений, которые у вас есть на U и давайте создадим тип, который удовлетворяет их. Это известно как образец из U,

Сначала я подумал, что «любой тип» будет полурегулярным. Тип, который является конструируемым по умолчанию, копируемым и назначаемым. Все нормальные вкусности:

namespace archetypes {
// private, only used for concept definitions, never in real code
struct Semiregular { };
}

archetypes::Semiregular это конкретный тип, поэтому мы можем использовать его для построения концепции:

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
requires(HKT<T> h, archetypes::Semiregular r) {
{h.F(r)} -> HKT<archetypes::Semiregular>
};

archetypes::Semiregular это частный тип. Это не должно быть известно HKTи так, если h.F(r) правильно сформирован и возвращает тип, преобразуемый в HKT<archetypes::Semiregular>, это почти наверняка шаблон функции-члена.

Вопрос в том, хороший ли это архетип? Нужны ли нам U быть полурегулярным, или неправильные типы будут работать тоже? Меньше операций, которые вы необходимость, меньшее количество должно присутствовать в вашем архетипе. Может быть, все, что вам нужно, это U подвижный:

namespace archetypes {
// private, only used for concept definitions, never in real code
struct Semiregular { };

struct Moveable {
Moveable() = delete;
Moveable(Moveable&& ) noexcept(false);
Moveable(Moveable const& ) = delete;
~Moveable() = default;

Moveable& operator=(Moveable const& ) = delete;
Moveable& operator=(Moveable&& ) noexcept(false);
};
}

template <template <class> class HKT, class T>
concept bool HKTWithTemplateMemberFunctionF =
requires(HKT<T> h, archetypes::Moveable m) {
{ h.F(m) } -> HKT<archetypes::Moveable>
};

Мы тестируем ту же идею — вызывая F() с типом, который не известен и исключая возвращаемый тип, чтобы отразить это, следовательно, требуя, чтобы это был шаблон функции. Но теперь мы даем меньше функциональности для типа. Если F() работает на любой, это будет работать на archetypes::Moveable,

Продолжайте повторять эту идею, пока вы действительно не уменьшите требуемую функциональность до минимума. Может быть, вам даже не нужен архетип, чтобы быть разрушаемым? Писать архетипы сложно, но в подобных случаях важно правильно понять.

2

Другие решения

Короче говоря, прямо сейчас вы (я?) Должны предоставить конкретный U:

template <template <class> class HKT, class T, class U = T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) { // HKT<T> is a type, h is an object
h.F(std::declval<U>()) -> HKT<U>;
}
}

так как компилятор не может доказать для всех типов U может существовать, что шаблон функции-члена будет работать, то есть следующее безнадежно:

template <template <class> class HKT, class T>
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
template<class U>  // for all those Us that haven't been written yet...
h.F(std::declval<U>()) -> HKT<U>;
}
}

В гипотетической, разработанной за 5 минут концептуальной реализации, где мы можем ограничить U только немного:

template <template <class> class HKT, class T,
InputIterator U = InputIterator()  /* imaginary syntax */ >
concept HKTWithTemplateMemberFunctionF {
return requires(HKT<T> h) {
h.F(std::declval<U>()) -> HKT<U>; // Is InputIterator enough to instantiate F?
}
}

компилятор должен будет только проверить, если модель InputIterator достаточно, чтобы создать экземпляр h.F, что возможно, даже если h.F не ограничен! Дополнительно предоставляя U просто проверяет, что он моделирует InputIteratorнет необходимости даже пытаться проверить h.F за U поскольку InputIterator достаточно. Это может быть использовано для оптимизации производительности во время компиляции и …

…вероятно, будет удивительным образом взаимодействовать с SFINAE, так как AFAIK вы можете иметь перегруженную концепцией функцию (например, для InputIterator) который принимает все входные итераторы кроме этого (СФИНАЕ! Зачем кому-то это делать ?!), и, таким образом, он может пройти проверку концепции, но взорвать во время создания экземпляра … грустно.

7

По вопросам рекламы [email protected]