19.2 – Шаблонные параметры, не являющиеся типами данных
В предыдущих уроках вы узнали, как использовать шаблонные параметры типа для создания функций и классов, не зависящих от типа. Шаблонный параметр типа – это тип-заполнитель, который заменяет тип, переданный в качестве аргумента.
Однако шаблонные параметры типа – не единственный доступный тип параметров шаблонов. Шаблоны классов и функций могут использовать другой тип шаблонного параметра, известный как параметр, не являющийся типом (non-type parameter).
Параметры, не являющиеся типами
Шаблонный параметр, не являющийся типом, – это параметр шаблона, в котором тип параметра предопределен, и он заменяется значением constexpr
, переданным в качестве аргумента.
Параметр, не являющийся типом, может быть любого из следующих типов:
- целочисленный тип;
- перечисление;
- указатель или ссылка на объект класса;
- указатель или ссылка на функцию;
- указатель или ссылка на функцию-член класса;
std::nullptr_t
;- тип с плавающей запятой (начиная с C++20).
В следующем примере мы создаем класс нединамического (статического) массива, который использует как параметр типа, так и параметр, не являющийся типом. Параметр типа управляет типом данных статического массива, а целочисленный параметр, не являющийся типом, определяет размер статического массива.
#include <iostream>
// size является целочисленным параметром, не являющимся типом
template <typename T, int size>
class StaticArray
{
private:
// Параметр, не являющийся типом, управляет размером массива
T m_array[size] {};
public:
T* getArray();
T& operator[](int index)
{
return m_array[index];
}
};
// Показываем, как функция для класса с параметром,
// не являющимся типом, определяется вне класса
template <typename T, int size>
T* StaticArray<T, size>::getArray()
{
return m_array;
}
int main()
{
// объявляем массив из int с местом для 12 целых чисел
StaticArray<int, 12> intArray;
// Заполняем его по порядку, а затем
// распечатываем в обратном направлении
for (int count { 0 }; count < 12; ++count)
intArray[count] = count;
for (int count { 11 }; count >= 0; --count)
std::cout << intArray[count] << ' ';
std::cout << '\n';
// объявляем буфер для double с местом для 4 значений double
StaticArray<double, 4> doubleArray;
for (int count { 0 }; count < 4; ++count)
doubleArray[count] = 4.4 + 0.1 * count;
for (int count { 0 }; count < 4; ++count)
std::cout << doubleArray[count] << ' ';
return 0;
}
Этот код печатает следующее:
11 10 9 8 7 6 5 4 3 2 1 0
4.4 4.5 4.6 4.7
В приведенном выше примере примечательно то, что нам не нужно динамически размещать переменную-член m_array
! Это связано с тем, что для любого заданного экземпляра класса StaticArray
размер должен быть constexpr
(константой времени компиляции). Например, если вы создаете экземпляр StaticArray<int, 12>
, компилятор заменит size
на 12. Таким образом, m_array
имеет тип int[12]
, который может размещаться статически.
Эта функциональность используется классом стандартной библиотеки std::array
. Когда вы размещаете std::array<int, 5>
, int
– это параметр типа, а 5 – это параметр, не являющийся типом!
Обратите внимание, что если вы попытаетесь создать экземпляр шаблона с параметром, не являющимся типом, со значением, отличным от constexpr
, это не сработает:
template <int size>
class Foo
{
};
int main()
{
int x{ 4 }; // x не является constexpr (константой времени компиляции)
Foo<x> f; // ошибка: аргумент шаблона, не являющийся типом, должен быть constexpr
return 0;
}
В таком случае компилятор выдаст ошибку.