¿Qué no se puede implementar sin ganchos de compilador?


C++11 proporciona <type_traits> estándar.

¿Cuáles de ellos son imposibles de implementar sin ganchos de compilador?

Author: Community, 2013-11-25

2 answers

He escrito una respuesta completa aquí - es un trabajo en progreso, así que estoy dando el hipervínculo autorizado a pesar de que estoy cortando y pegando el texto en esta respuesta.

También vea la documentación de libc++sobre Características de tipo diseño intrínseco.

Is_union

is_union consulta un atributo de la clase que no se expone a través de ningún otro medio; en C++, todo lo que se puede hacer con una clase o estructura, también se puede hacer con una unión. Este incluir heredar y tomar punteros de miembros.

Is_aggregate, is_literal_type, is_pod, is_standard_layout, has_virtual_destructor

Estos rasgos consultan atributos de la clase que no se exponen a través de ningún otro medio. Esencialmente, una estructura o clase es una "caja negra"; el lenguaje C++ no nos da forma de abrirlo y examinar sus miembros de datos para averiguar si son todos los tipos de POD, o si cualquiera de ellos es private, o si la clase tiene algún constructor constexpr (la clave requisito para is_literal_type).

Is_abstract

is_abstract es un caso interesante. La característica definitoria de un resumen el tipo de clase es que no puede obtener un valor de ese tipo; por ejemplo, es mal formado para definir una función cuyo parámetro o tipo de retorno es abstracto, y está mal formado para crear un tipo de matriz cuyo tipo de elemento es abstracto. (Curiosamente, si T es abstracto, entonces SFINAE se aplicará a T[] pero no a T(). Que es, es aceptable crear el tipo de un función con un tipo de retorno abstracto; está mal formado definir una entidad de tal tipo de función.)

Así que podemos acercarnos mucho a una correcta implementación de is_abstract usando este enfoque 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> {};

Sin embargo, hay un defecto! Si T es en sí misma una clase de plantilla, como vector<T> o basic_ostream<char>, entonces simplemente formar el tipo T[] es aceptable; en un contexto sin evaluar esto no hará que el compilador vaya a instanciar el cuerpo de T, y por lo tanto el compilador no detectará la falta de forma de el tipo de matriz T[]. Así que las SFINAE no sucederán en ese caso, y dar la respuesta incorrecta para is_abstract<basic_ostream<char>>.

Esta peculiaridad de la instanciación de plantillas en contextos no evaluados es la única razón que los compiladores modernos proporcionan __is_abstract(T).

Is_final

is_final consulta un atributo de la clase que no se expone a través de ningún otro medio. Específicamente, la base-specifier-list de una clase derivada no es un SFINAE contexto; no podemos explota enable_if_t para preguntar "¿puedo crear una clase derivada de T?"porque si no se puede crear tal clase, será un error difícil.

Is_empty

is_empty es un caso interesante. No podemos simplemente preguntar si sizeof (T) == 0 porque en C++ no se permite que ningún tipo tenga tamaño 0; incluso una clase vacía tiene sizeof (T) == 1. "Vacío" es lo suficientemente importante como para merecer un rasgo de tipo, sin embargo, debido a la Base Vacía Optimización: todos los compiladores suficientemente modernos trazarán los dos clases

struct Derived : public T { int x; };

struct Underived { int x; };

Idénticamente; es decir, no dispondrán ningún espacio en Derived para el vacío T subobjeto. Esto sugiere una forma en que podríamos probar el "vacío" en C++03, al menos en todos los compiladores suficientemente modernos: simplemente defina las dos clases anteriores y pregunte si sizeof (Derived) == sizeof (Underived). Desafortunadamente, a partir de C++11, este truco ya no funciona, porque T podría ser final, y la "final -ess" de una clase el tipo no está expuesto por ningún otro medio! Proveedores de compiladores que implementan final también debe exponer algo como __is_empty(T) para el beneficio de la biblioteca estándar.

Is_enum

is_enum es otro caso interesante. Técnicamente, podríamos implementar este tipo de rasgo por la observación de que si nuestro tipo T es no un tipo fundamental, un tipo de matriz, un tipo de puntero, un tipo de referencia, un puntero miembro, una clase o unión, o una función tipo, luego por proceso de eliminación debe ser un tipo de enumeración. Sin embargo, este deductivo el razonamiento se descompone si el compilador pasa a apoyar cualquier otro tipo de no caer en las categorías anteriores. Por esta razón, los compiladores modernos exponen __is_enum(T).

Un ejemplo común de un tipo soportado que no cae en ninguna de las categorías anteriores sería __int128_t. libc++ realmente detecta la presencia de __int128_t e incluye en la categoría de "tipos integrales" (lo que lo convierte en un "tipo fundamental" en la anterior categorización), pero nuestra simple implementación no lo hace.

Otro ejemplo sería vector int, en compiladores compatibles con extensiones vectoriales de Altivec; este tipo es más obviamente "no integral", sino también "no cualquier otra cosa", y la mayoría ciertamente no es un tipo de enumeración!

Is_trivially_constructible, is_trivially_assignable

La trivialidad de la construcción, asignación y destrucción son todos atributos de la clase que no están expuestos a través de ningún otro medio. Note que con esta fundación no necesitamos ninguna magia adicional para consultar la trivialidad de default construcción, copiar construcción, mover asignación, y así sucesivamente. En su lugar, is_trivially_copy_constructible<T> se implementa en términos de is_trivially_constructible<T, const T&>, y así sucesivamente.

Is_trivially_destructible

Por razones históricas, el nombre de este compilador builtin no es {[38]]} sino más bien __has_trivial_destructor(T). Además, resulta que el builtin evalúa a true incluso para un tipo de clase con un destructor eliminado! Así que primero necesitamos para comprobar que el tipo es destructible; y luego, si lo es, podemos preguntar al magia incorporada si ese destructor es realmente trivial.

Underlying_type

El tipo subyacente de una enumeración no se expone a través de ningún otro medio. Puedes acercarte tomando sizeof(T) y comparándolo con los tamaños de todos los tipos conocidos, y preguntando para la signedness del tipo subyacente vía T(-1) < T(0); pero ese enfoque todavía no puede distinguir entre los tipos de subyacentes int y long en plataformas en las que los tipos tienen el mismo ancho (ni entre long y long long en plataformas donde esos los tipos tienen el mismo ancho).

 11
Author: Quuxplusone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-13 19:05:01

Per lastest boost documentation que también es un poco viejo pero creo que todavía es válido

Soporte para Intrínsecos del compilador

Hay algunos rasgos que no se pueden implementar dentro del lenguaje C++ actual: para hacer que estos rasgos "funcionen" con tipos definidos por el usuario, se requiere algún tipo de ayuda adicional del compilador. Actualmente (abril de 2008) Visual C++ 8 y 9, GNU GCC 4.3 y MWCW 9 proporcionan al menos algunos de los intrínsecos necesarios, y otros compiladores sin duda seguirá a su debido tiempo.

Las Siguientes clases de traits siempre necesitan soporte de compilador para hacer lo correcto para todos los tipos (pero todos tienen posiciones de reserva seguras si este soporte no está disponible):

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

Las siguientes clases de traits no se pueden implementar portablemente en el lenguaje C++, aunque en la práctica, las implementaciones de hecho hacen lo correcto en todos los compiladores que conocemos:

is_empty
is_polymorphic

Las siguientes clases de rasgos dependen de uno o más de lo anterior:

is_class 
 18
Author: deepmax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-24 22:52:08