Подробности про std::conjunction vs &&



В этом посте я рассказывал про замечательные тайптрейты std::conjunction, std::disjunction. Они позволяют компоновать несколько трейтов в одну логическую последовательность. Там же я рассказывал про то, что до них для этих целей использовались операторы &&, ||. Безусловно, человеческим языком обозванные сущности проще воспринимаются, чем какие-то символы. Но неужели это все различия? Какая-то вялая причина, чтобы вводить в стандарт эти трейты.



И правда, различия есть. Еще какие!



Прежде, чем начать разбирать их, нужно поподробнее рассмотреть эти метаклассы, потому что оттуда все различия. Рассматривать будем на примере std::conjunction, ибо у них все очень похоже.



Примерно так этот класс может быть реализован



template<class...> struct conjunction : std::true_type

template<class B1> struct conjunction<B1> : B1 {};

template<class B1, class... Bn>

struct conjunction<B1, Bn...>

: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};




Специализация std::conjunction<B1, ..., Bn> имеет публичную базу, которая варьируется в зависимости от аргументов



Если их нет, то базовым классом для std::conjunction будет std::true_type.



Если они есть, то базой будет первый тип Bi из B1, ..., Bn, для которого bool(Bi::value) == false.



Если для всех Bi bool(Bi::value) == true, тогда базой будет Bn.



Кстати std::conjunction не обязательно по итогу наследуется либо от std::true_type, либо от std::false_type: она просто наследует от первого B, для которого его ::value, явно преобразованное в bool, является ложным, или от самого последнего B, когда все они преобразуются в true. То есть это самое value может быть даже не булевым значением, а например числом. Вот так:



std::conjunction<std::integral_constant<int, 2>,std::integral_constant<int, 4>>::value == 4 - верно!




И вот в этом весь прикол. std::conjunction - это вычисление по короткой схеме! То есть как только мы нашли такой Bi, что для него bool(Bi::value) == false, компилятор прекращает дальше инстанцировать вглубь рекурсии и однозначно определяет тип базового класса, а значит и поля value.



И как раз таки в этом аспекте метаклассы conjunction и disjunction отличаются от обычных && и ||. Но об этом мы поговорим завтра.



Understand true essence of things. Stay cool.



#cpp17 #template #hardcore