Объединения условий в enable_if



Иногда мы хотим сильно ограничить свойства типов, с которыми мы хотим инстанцировать шаблон. Например, тип должен быть default-constructed и иметь оператор сравнения. У нас есть для этого метафункция std::enable_if, которая позволяет нам проверять наличие свойств у типов. Но вот незадача, как проверить два условия одновременно? Я хочу и то, и то.



Ранее для этого использовались обычные операторы && и || между тайптрейтами.



Однако в С++17 появились специальные метаклассы, которые позволяют комбинировать условия. Это



• template<class... B> struct conjunction; - логическое И

• template<class... B> struct disjunction; - логическое ИЛИ

• template<class B> struct negation; - логичесткое НЕ



Эти трейты имеют подходящие осмысленные имена, поэтому их использование повышает читаемость кода.



Например, вы можете сочетать эти метафункции с variadic шаблонами

Тогда может появиться что-то такое:



template<typename... Ts>

std::enable_if_t<std::conjunction_v<std::is_same<int, Ts>...>> PrintIntegers(Ts ... args)

{

(std::cout << ... << args) << 'n';

}




В функцию PrintIntegers мы можем передать сколько угодно(почти) аргументов и все они будут проверяться на соответствие целочисленному типу



Или например вы хотите принтовать только числа? Тогда мы идем к вам:



template<typename T, typename = std::enable_if_t<std::disjunction_v<std::is_integral<T>, std::is_floating_point<T>>>>

void PrintValue(T value)

{

std::cout << "Value: " << value << std::endl;

}





Если тип - не целочисленный и не с плавающей точкой, то такая перегрузка будет отбрасываться.



Ну и например вы хотите, чтобы функция для вывода в консоль не принимала аргумент-указатель. Тоже можно сделать.



template<typename T, typename = std::enable_if_t<std::negation_v<std::is_pointer<T>>>> 

void PrintValue(T value)

{

std::cout << "Value: " << value << std::endl;

}




Такие вот удобные метафункции.



Create complex conditions. Stay cool.



#cpp17 #template