Ответ 1
Я написал полный ответ здесь - это незавершенная работа, поэтому я даю авторитетную гиперссылку, m резка и вставка текста в этот ответ.
Также см. документацию на libС++ на Свойство типизированных объектов.
is_union
is_union
запрашивает атрибут класса, который не подвергается никаким другим способом;
в С++, все, что вы можете сделать с классом или структурой, вы также можете сделать с объединением. Эта
включает наследование и принятие указателей элементов.
is_aggregate, is_literal_type, is_pod, is_standard_layout, has_virtual_destructor
Эти атрибуты запрашивают атрибуты класса, которые не отображаются никакими другими способами.
По существу, структура или класс - это "черный ящик"; язык С++ не дает нам
взломать его и изучить его членов данных, чтобы узнать, все ли они типы POD, или если
любой из них private
, или если класс имеет любые конструкторы constexpr (ключ
требование для is_literal_type
).
is_abstract
is_abstract
- интересный случай. Определяющая характеристика абстрактного
тип класса заключается в том, что вы не можете получить значение этого типа; так, например, это
плохо сформированный для определения функции, параметр или возвращаемый тип которой является абстрактным, и
он плохо сформирован для создания типа массива, тип элемента которого является абстрактным.
(Как ни странно, если T
является абстрактным, то SFINAE будет применяться к T[]
, но не к T()
.
является допустимым создание типа функции с абстрактным возвращаемым типом;
он плохо сформирован для определения объекта такого типа функции.)
Таким образом, мы можем приблизиться к правильной реализации is_abstract
, используя
этот подход SFINAE:
template<class T, class> struct is_abstract_impl : true_type {};
template<class T> struct is_abstract_impl<T, void_t<T[]>> : false_type {};
template<class T> struct is_abstract : is_abstract_impl<remove_cv_t<T>, void> {};
Однако есть недостаток! Если T
сам является классом шаблона, например vector<T>
или basic_ostream<char>
, то приемлемо просто формирование типа T[]
; в
необоснованный контекст, это не приведет к тому, что компилятор будет запускать экземпляр
body T
, и поэтому компилятор не обнаружит неправильную форму
тип массива T[]
. Таким образом, SFINAE не произойдет в этом случае, и мы будем
введите неправильный ответ для is_abstract<basic_ostream<char>>
.
Эта причуда создания экземпляра шаблона в необоснованных контекстах является единственной причиной
что современные компиляторы предоставляют __is_abstract(T)
.
is_final
is_final
запрашивает атрибут класса, который не отображается с помощью каких-либо других средств.
В частности, базовый-спецификатор-список производного класса не является SFINAE-контекстом; мы не можем
exploit enable_if_t
спросить "могу ли я создать класс, полученный из T
?"? потому что если мы
не может создать такой класс, это будет трудная ошибка.
IS_EMPTY
is_empty
- интересный случай. Мы не можем просто спросить, существует ли sizeof (T) == 0
, потому что
в С++ ни один тип никогда не может иметь размер 0; даже пустой класс имеет sizeof (T) == 1
.
"Пустота" достаточно важна, чтобы заслужить черту типа, хотя из-за Пустой Базы
Оптимизация: все достаточно современные компиляторы выведут два класса
struct Derived : public T { int x; };
struct Underived { int x; };
тождественно; то есть они не будут выкладывать какое-либо пространство в Derived
для пустого
T
подобъект. Это говорит о том, как мы могли бы проверить "пустоту" на С++ 03, по крайней мере
на всех достаточно современных компиляторах: просто определите два класса выше и спросите
будь то sizeof (Derived) == sizeof (Underived)
. К сожалению, с С++ 11 это
трюк больше не работает, потому что T
может быть окончательным, а "окончательная версия" класса
тип не подвергается никакими другими способами! Поэтому производители компиляторов, которые реализуют final
должен также выставлять что-то вроде __is_empty(T)
в пользу стандартной библиотеки.
is_enum
is_enum
- еще один интересный случай. Технически мы могли бы реализовать эту черту типа
что если наш тип T
не является фундаментальным типом, тип массива,
тип указателя, ссылочный тип, указатель члена, класс или объединение или функция
тип, то по процессу исключения он должен быть типом перечисления. Однако этот дедуктивный
рассуждение ломается, если компилятор поддерживает другие типы, не падающие
в вышеуказанные категории. По этой причине современные компиляторы выставляют __is_enum(T)
.
Обычный пример поддерживаемого типа, не попадающий ни в одну из вышеуказанных категорий
будет __int128_t
. libС++ фактически обнаруживает наличие __int128_t
и включает
он относится к категории "интегральных типов" (что делает его "основным типом" в приведенном выше
категоризация), но наша простая реализация не делает.
Другим примером может быть vector int
, для компиляторов, поддерживающих Altivec vector extensions;
этот тип более явно "не является целым", но также "не что иное", и большинство
конечно, не тип перечисления!
is_trivially_constructible, is_trivially_assignable
Тривиальность конструкции, назначения и разрушения - все атрибуты
класса, которые не подвергаются каким-либо другим средствам. Обратите внимание, что с этим основанием
нам не нужна дополнительная магия для запроса тривиальности построения по умолчанию,
копирование, перенос задания и т.д. Вместо,
is_trivially_copy_constructible<T>
реализуется в терминах
is_trivially_constructible<T, const T&>
и т.д.
is_trivially_destructible
По историческим причинам имя этого компилятора не является __is_trivially_destructible(T)
а скорее __has_trivial_destructor(T)
. Кроме того, оказывается, что встроенный
оценивает true
даже для типа класса с удаленным деструктором! Поэтому нам сначала нужно
проверить, что тип разрушен; и тогда, если это так, мы можем спросить волшебство
является ли этот деструктор действительно тривиальным.
underlying_type
Основной тип переименования не отображается никакими другими способами. Вы можете приблизиться
взяв sizeof(T)
и сравнивая его с размерами всех известных типов, и, попросив
для подписи базового типа через T(-1) < T(0)
; но этот подход все еще
не могут различать базовые типы int
и long
на платформах, где эти
типы имеют одинаковую ширину (и между long
и long long
на платформах, где эти
типы имеют одинаковую ширину).