Здравствуйте, so5team, Вы писали:
S>S>template<std::integral T>
S>void f1(T /*v*/) {}
S>void f2(std::integral auto /*v*/) {}
S>template<typename T>
S>void f3(T /*v*/) requires std::integral<T> {}
S>
S>Попробуйте объяснить какой из этих вариантов наиболее удобен и почему.
Для простейших случаев второй, вестимо.
Для нескольких requires подходит только последний.
Насчёт того, что с синтаксисом что-то надо делать — согласен.
Второй вариант уже что-то делает с синтаксисом, но он работает только для аргументов-типов, а хотелось бы еще с константами времени компиляции.
Например что-то типа такого:
void fn<int c>(auto arg1, auto arg2) {}
Эквивалент
template<int c, typename T1, typename T2>
void fn(T1 arg1, T2 arg2) {}
Более сложный вариант:
void fn<int c, typename T3<int, typename T4>>(auto arg1, auto arg2) {}
Или даже сделать typename опциональным-дефолтным, тогда:
void fn<int c, T3<int, T4> >(auto arg1, auto arg2) {}
(хотя, здесь такие же опасения, как для var/let — требуется ли явное указание, что вводится новая сущность, чтобы избежать ошибок переприсвоения уже имеющейся переменной, в данном случае — переменной типа, которая внезапно может оказаться уже определённым символом в данном контексте, таким же int, например)
Эквивалент
template<int c, template<int, typename T4> typename T3, typename T1, typename T2>
void fn(T1 arg1, T2 arg2) {}
При наличи конструкций ->decltype() предварительный синтаксис template<> уже не столь нужен, т.к. есть альтернативный способ вывода типа результата шаблонных ф-ий.