Как в C++ 20 ограничить шаблон целочисленными типами (int, long, long long, unsigned long long)?

В последней (20-й) версии С++ появились конструкции которые позволяют каким то образом ограничить template. Однако как это правильно сделать я пока не могу разобраться.

Помогите мне ограничить функцию только целочисленными типами.

/*
 * For a given number the function returns unsorted vector of it's factors.
 * Time Complexity of the algorithm is O(sqrt(N)).
 */
template <class T>
vector<T> factors(T number) {
    vector<T> divisors;

    for (T divisor = sqrt(number); divisor > 0; --divisor) {
        if (number % divisor == 0) {
            divisors.push_back(divisor);
            T paired_divisor = number / divisor;
            if (paired_divisor != divisor) {
                divisors.push_back(paired_divisor);
            }
        }
    }
    return divisors;
}


Ответы (1 шт):

Автор решения: HolyBlackCat

Под целочисленные типы в стандартной библиотеке есть готовый концепт. Пользоваться им можно вот так:

#include <concepts>

template <std::integral T>
vector<T> factors(T number) {...}

Свои концепты можно делать так:

template < параметры (например typename T) > concept имя = булево выражение (константа времени компиляции).

Например, std::integral задается как template <typename T> concept integral = std::is_integral_v<T>;.

Для использования концептов есть куча вариантов синтаксиса. Попробую сжато описать:

  • Просто как булево выражение, например if (std::integral<T>)
  • template <std::integral T>
    Обратите внимание, что тут не указывается шаблонный параметр концепта - в него ставится T. Если параметров больше одного, то все кроме первого нужно указывать самому, например template <std::derived_from<X> T> принимает любые T, для которых истинно std::derived_from<T, X>.
  • В C++20 разрешили auto в параметрах функций (как в лямбдах), что неявно создает шаблон.
    Теперь и там и там можно делать еще и так: void foo(std::integral auto x).
  • В целом в C++20 можно вешать произвольное условие-ограничение на шаблон, в виде булевого выражения. template <std::integral T> эквивалентно template <typename T> requires std::integral<T>. Для шаблонов функций requires можно указывать не только после шаблонных параметров, но и после обычных параметров - разница минимальная.
  • Возможно еще что-то забыл.
→ Ссылка