Во-первых, делая это:
requires(Operation&& op, Integral&& a, Integral&& b)
вы, вероятно, имели в виду это:
requires(Operation op, Integral auto a, Integral auto b)
то есть, чтобы использовать ограничение типа auto
форма описателя типа заполнителя.
Однако параметры auto
создают шаблоны функций, а заполнитель auto
является эквивалентом фактического параметра шаблона придуманного типа этого шаблона функции, поэтому он может отображаться только в контекстах, где происходит вывод типа из инициализатора, и это означает, что:
void foo(auto x);
является эквивалентом:
template <typename T>
void foo(T x);
Аналогично,
void bar(Integral auto x);
является эквивалентом:
template <Integral T>
void bar(T x);
Такое преобразование не применимо к выражению requires, т. е. отсутствует инициализатор, тип которого можно было бы определить, и поэтому нельзя использовать заполнитель auto
в объявлении параметра списка параметров требований. Эти параметры, как сказано в стандарте, «используются только в качестве обозначения с целью определения требований». Таким образом, вместо этого вам необходимо явно указать все аргументы шаблона, которые необходимы для работы концепции, например:
template <typename Operation, typename Lhs, typename Rhs>
concept CalculatorOperation = requires(Operation op, Lhs a, Rhs b)
{
{ op(a, b) } -> Integral;
};
template <typename Operation>
constexpr Integral auto Calculator(Integral auto first, Integral auto second)
requires CalculatorOperation<Operation, decltype(first), decltype(second)>
{
return Operation{}(first, second);
}
Также обратите внимание, что я удалил &&
из каждого объявления параметра в списке параметров требований. Все выражение requires - это неоцененный контекст, и аргументы фактически не передаются.