Шаблоны enable_if
Ограничивайте шаблоны классическим способом
«Шаблоны enable_if» — бесплатный урок C++ Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Что такое enable_if
std::enable_if — это небольшой типовой признак, который условно определяет член type. Если условие истинно, тип существует; если ложно, окружающий шаблон удаляется с помощью SFINAE.
- Находится в
<type_traits>. - Это классический инструмент для ограничения шаблонов.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
doubleIt(T v) { return v * 2; }
int main() {
std::cout << doubleIt(21) << '\n';
return 0;
}Как это работает
enable_if<Cond, T>::type равен T только тогда, когда Cond истинно. Если условие ложно, члена type нет, поэтому подстановка завершается неудачей.
#include <iostream>
#include <type_traits>
int main() {
std::cout << std::boolalpha;
std::cout << std::is_same<std::enable_if<true, int>::type, int>::value << '\n';
return 0;
}enable_if в возвращаемом типе
Чаще всего его размещают в возвращаемом типе. Функция существует только тогда, когда условие выполнено.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
half(T v) { return v / 2; }
int main() {
std::cout << half(5.0) << '\n';
return 0;
}enable_if как параметр шаблона
Другой идиоматичный вариант помещает enable_if в параметр шаблона со значением по умолчанию, оставляя возвращаемый тип чистым.
#include <iostream>
#include <type_traits>
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
T triple(T v) { return v * 3; }
int main() {
std::cout << triple(4) << '\n';
return 0;
}Сокращённая запись enable_if_t
В C++14 появился псевдоним std::enable_if_t<Cond, T>, позволяющий не писать громоздкую конструкцию typename ...::type.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
negate(T v) { return -v; }
int main() {
std::cout << negate(8) << '\n';
return 0;
}Объединение двух перегрузок
Задайте взаимодополняющие условия, чтобы для любого типа была допустима ровно одна перегрузка и неоднозначность не возникала.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_integral<T>::value, const char*>
category() { return "integer"; }
template <typename T>
std::enable_if_t<!std::is_integral<T>::value, const char*>
category() { return "non-integer"; }
int main() {
std::cout << category<int>() << ' ' << category<double>() << '\n';
return 0;
}Объединение условий
Используйте логические операторы над значениями типовых признаков, чтобы выразить более сложные ограничения, например «целочисленный и знаковый».
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, T>
magnitude(T v) { return v < 0 ? -v : v; }
int main() {
std::cout << magnitude(-12) << '\n';
return 0;
}Ограничение конструктора
Можно разместить enable_if в параметре шаблона конструктора, чтобы разрешить его использование только для совместимых типов.
#include <iostream>
#include <type_traits>
struct Wrapper {
int value;
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
Wrapper(T v) : value(static_cast<int>(v)) {}
};
int main() {
Wrapper w(42);
std::cout << w.value << '\n';
return 0;
}Проблема аргумента шаблона по умолчанию
Две перегрузки, различающиеся только параметром шаблона enable_if со значением по умолчанию, воспринимаются как повторные определения. Задайте им разную форму, например разместите одно условие в возвращаемом типе.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
printKind() { std::cout << "int\n"; }
template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
printKind() { std::cout << "float\n"; }
int main() {
printKind<int>();
printKind<double>();
return 0;
}Сравнение с концепциями
Концепции C++20 заменяют большую часть применений enable_if гораздо более понятным синтаксисом, но enable_if по-прежнему необходим в коде до C++20 и в некоторых особых случаях.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T>
square(T v) { return v * v; }
int main() {
std::cout << square(6) << ' ' << square(1.5) << '\n';
return 0;
}Полный пример
Объединим всё изученное: семейство функций, которое по-разному форматирует целые числа и числа с плавающей точкой, выбирая вариант на этапе компиляции.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
format(T v) { std::cout << "int[" << v << "]\n"; }
template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
format(T v) { std::cout << "float(" << v << ")\n"; }
int main() {
format(10);
format(3.5);
return 0;
}Быстрая проверка
Проверьте, насколько хорошо Вы понимаете enable_if.
Итоги
Вы изучили классические шаблоны с enable_if:
- он определяет
::typeтолько при истинном условии, обеспечивая SFINAE - размещается в возвращаемом типе или параметре шаблона со значением по умолчанию
- используйте псевдоним
enable_if_tи объединяйте типовые признаки логическими операторами - в C++20 его заменили концепции, но он по-прежнему широко используется
На этом курс по специализации шаблонов и SFINAE завершён.
Часто задаваемые вопросы
Урок «Шаблоны enable_if» бесплатный?
Да — полный текст урока «Шаблоны enable_if» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Шаблоны enable_if»?
Ограничивайте шаблоны классическим способом Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Шаблоны enable_if»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Полная специализация
- Частичная специализация
- SFINAE
- Шаблоны enable_if