Подтвердить что ты не робот

Какие <type_traits> не могут быть реализованы без перехватчиков компилятора?

С++ 11 предоставляет стандартный <type_traits>.

Какие из них невозможно реализовать без компиляторов?

  • Примечание 1: с помощью компилятора я имею в виду любую нестандартную языковую функцию, такую ​​как __is_builtin....
  • Примечание 2: многие из них могут быть реализованы без перехвата (см. главу 2 С++ Template Metaprogramming и/или глава 2 из Современный дизайн С++).
  • Примечание 3: ответ spraff в этом предыдущем вопросе цитирует N2984 где некоторые черты типа содержат следующее примечание: считается, что требуется поддержка компилятора (спасибо sehe).
4b9b3361

Ответ 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 на платформах, где эти типы имеют одинаковую ширину).

Ответ 2

В течение последней дополнительной документации, которая также немного устарела, но я думаю, что она действительна еще

Поддержка встроенных функций компилятора

Есть некоторые черты, которые не могут быть реализованы на текущем языке С++: чтобы эти черты "просто работали" с определенными пользователем типами, требуется какая-то дополнительная помощь от компилятора. В настоящее время (апрель 2008 г.) Visual С++ 8 и 9, GNU GCC 4.3 и MWCW 9 обеспечивают, по крайней мере, некоторые из необходимых встроенных функций, а другие компиляторы, несомненно, последуют в свое время.

Следующие классы признаков всегда нуждаются в поддержке компилятора для правильной работы для всех типов (но у всех есть надежные резервные позиции, если эта поддержка недоступна):

is_union
is_pod
has_trivial_constructor
has_trivial_copy
has_trivial_move_constructor
has_trivial_assign
has_trivial_move_assign
has_trivial_destructor
has_nothrow_constructor
has_nothrow_copy
has_nothrow_assign
has_virtual_destructor

Следующие классы признаков не могут быть реализованы на языке С++, но на практике реализация на самом деле делает правильные вещи для всех компиляторов, о которых мы знаем:

is_empty
is_polymorphic

Следующие классы признаков зависят от одного или нескольких из перечисленных выше:

is_class