Перейти к содержанию

Последнее обновление:2026-10-12

Шаблоны переменных в C++17

C++那些事 — шаблоны переменных

При написании шаблонного кода нам часто требуется определять и использовать различные типовые признаки (type traits). Эти признаки обычно применяются для проверки типов на этапе компиляции; примерами служат предоставляемые STL std::is_const, std::is_same и std::is_reference. С их помощью разработчики могут принимать решения на этапе компиляции, опираясь на свойства типов.

В этой статье рассказывается, как использовать шаблоны переменных C++14 (variable templates) для упрощения написания типовых признаков, а также приводится простой пример.

C++11

В C++11 написание типовых признаков обычно требует использования struct или std::integral_constant для инкапсуляции логического значения. Например, чтобы написать типовой признак is_float, проверяющий, является ли тип float, можно реализовать его так:

template <typename T>
struct is_float {
    static constexpr bool value = std::is_same<T, float>::value;
};

Или чуть лаконичнее — с помощью псевдонима типа и std::integral_constant:

template <typename T>
using is_float = std::integral_constant<bool, std::is_same<T, float>::value>;

Хотя это лаконичнее, чем использовать struct напрямую, в итоге мы всё равно определяем тип, а не получаем сразу логическое значение. Чтобы воспользоваться таким признаком, приходится получать логическое значение через член ::value:

template <typename T>
void test(T t){
    if (is_float<T>::value){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

Это работает, но остаётся два недостатка: 1. При каждом использовании типового признака приходится обращаться к ::value. 2. При определении нового типового признака необходимо объявлять новый тип или псевдоним типа, что увеличивает избыточность кода.

Появление шаблонов переменных в C++14

В C++14 появились шаблоны переменных, позволяющие определять переменные, зависящие от типа, напрямую. Эта возможность упростила написание типовых признаков: нам больше не нужно использовать псевдонимы типов или struct для инкапсуляции логического значения — можно определить само логическое значение.

Признак is_float в переписанном виде выглядит так:

template <typename T>
constexpr bool is_float = std::is_same<T, float>::value;

Такой способ выглядит более лаконичным и понятным, без лишнего кода. Использовать его тоже удобнее:

template <typename T>
void test(T t){
    if (is_float<T>){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

По сравнению с прежним вариантом при использовании шаблонов переменных больше не нужно обращаться к логическому значению через ::value, код стал проще и нагляднее.

Преимущества шаблонов переменных

  1. Меньше избыточного кода: шаблоны переменных позволяют напрямую определять зависящие от типов переменные, избегая избыточного кода инкапсуляции логического значения в struct или std::integral_constant.
  2. Лучшая читабельность: шаблоны переменных за счёт упрощения синтаксиса делают код яснее, особенно при написании большого числа типовых признаков.
  3. Поддержка специализации шаблонов: в отличие от шаблонов псевдонимов типов, шаблоны переменных можно специализировать — как полностью, так и частично. Мы можем определить отдельную логику для конкретных типов, не беспокоясь о расширяемости.

Дальнейшие улучшения в C++17

C++17 ещё больше расширил применение шаблонов переменных, предоставив для каждого типового признака вспомогательный шаблон переменной. Например, для std::is_same появился шаблон переменной std::is_same_v, упрощающий доступ к значению типового признака:

template <typename T>
constexpr bool is_float = std::is_same_v<T, float>;

Благодаря этому использование типовых признаков стало ещё лаконичнее: разработчики могут напрямую применять шаблоны переменных, не обращаясь каждый раз к ::value.

Практический пример

Эта возможность широко используется на практике; например, при выделении блоков памяти разного размера в базе данных мы можем инициализировать размеры блоков разных типов с помощью шаблонов.

template <typename T>
constexpr size_t block_size = 1024;  // 默认大小

template <>
constexpr size_t block_size<double> = 2048;  // double 类型需要更多内存

Похожие темы

Комментарии