0Pricing
C++ Academy · Урок

Шаблоны 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 — локальная установка не требуется.

Все уроки этого курса

  1. Полная специализация
  2. Частичная специализация
  3. SFINAE
  4. Шаблоны enable_if
← Назад к C++ Academy