c++ - ¿Std:: vector<T> es un `tipo definido por el usuario`?
c++11 language-lawyer (4)
Como los usuarios no definen
std::vector<int>
, ystd::vector<int>
no depende de ningún tipo que el usuario defina,std::vector<int>
no es un tipo definido por el usuario.
El argumento del contador lógico es que los usuarios definen std::vector<int>
. Verá que std::vector
es una plantilla de clase y, como tal, no tiene representación directa en el código binario.
En cierto sentido, obtiene la representación binaria mediante la instanciación de un tipo, por lo que la acción de declarar un objeto std::vector<int>
es lo que le da "alma" a la plantilla (perdón por el fraseo). En un programa donde nadie usa std::vector<int>
este tipo de datos no existe.
Por otro lado, siguiendo el mismo argumento, std::vector<T>
no es un tipo definido por el usuario, ni siquiera es un tipo, no existe; solo si queremos (instanciar un tipo), ordenará cómo se estructurará una estructura, pero hasta entonces solo podemos discutir sobre ella en términos de estructura , diseño , propiedades , etc.
Nota
El argumento anterior (sobre plantillas que no son código pero ... plantillas de código) puede parecer un poco superficial, pero dibuja su lógica, a partir de la introducción de Mayer en el libro de A. Alexandrescu Modern C ++ Design . La cita relativa allí, dice así:
Eventualmente, Andrei dirigió su atención al desarrollo de implementaciones basadas en plantillas de expresiones idiomáticas populares y patrones de diseño, especialmente los patrones GoF [*]. Esto condujo a una breve escaramuza con la comunidad Patterns, porque uno de sus principios fundamentales es que los patrones no se pueden representar en el código. Una vez que quedó claro que Andrei estaba automatizando la generación de implementaciones de patrones en lugar de tratar de codificar los patrones , esa objeción fue eliminada, y me complació ver que Andrei y uno de los GoF (John Vlissides) colaboraron en dos columnas en el Informe C ++ centrándose en el trabajo de Andrei.
En 17.6.4.2.1 / 1 y 17.6.4.2.1 / 2 del borrador actual, las restricciones estándar se aplican a las especializaciones inyectadas por los usuarios en el namespace std
estándar .
El comportamiento de un programa C ++ no está definido si agrega declaraciones o definiciones al espacio de nombres std o a un espacio de nombres dentro del espacio de nombres estándar, a menos que se especifique lo contrario. Un programa puede agregar una especialización de plantilla para cualquier plantilla de biblioteca estándar al espacio de nombre std solo si la declaración depende de un tipo definido por el usuario y la especialización cumple con los requisitos de biblioteca estándar para la plantilla original y no está explícitamente prohibida.
No puedo encontrar en qué parte del estándar está definida la frase tipo definido por el usuario .
Una de las opciones que he escuchado es que un tipo que no es std::is_fundamental
es un tipo definido por el usuario , en cuyo caso std::vector<int>
sería un tipo definido por el usuario .
Una respuesta alternativa sería que un tipo definido por el usuario es un tipo que define un usuario. Como los usuarios no definen std::vector<int>
, y std::vector<int>
no depende de ningún tipo que el usuario defina, std::vector<int>
no es un tipo definido por el usuario .
Un problema práctico que esto impacta es "¿puedes inyectar una especialización para std::hash
para std::tuple<Ts...>
en namespace std
? Ser capaz de hacerlo es algo conveniente - la alternativa es crear otro espacio de nombre donde construimos recursivamente nuestro hash para std::tuple
(y posiblemente otros tipos en std
que no tienen soporte para hash
), y si y solo si fallamos al encontrar un hash en ese espacio de nombres, recurrimos a std
.
Sin embargo, si esto es legal, entonces si y cuando el estándar agrega una especialización hash
para std::tuple
al namespace std
de namespace std
, el código que lo especializa ya estaría roto, creando una razón para no agregar tales especializaciones en el futuro.
Mientras estoy hablando de std::vector<int>
como ejemplo concreto, estoy tratando de preguntar si los tipos definidos en std
son tipos definidos por el usuario . Una pregunta secundaria es que, incluso si no, tal vez std::tuple<int>
convierta en un tipo definido por el usuario cuando es utilizado por un usuario (esto se vuelve resbaladizo: ¿qué ocurre si algo dentro de std
define std::tuple<int>
, y hash
especialización parcial para std::tuple<Ts...>
).
Actualmente hay un defecto abierto en este problema.
Cuando la cláusula 17 dice "definido por el usuario", significa "un tipo no definido en el estándar", por lo que std::vector<int>
no está definido por el usuario, ni std::string
, por lo que no puede especializar std::vector<int>
o std::vector<std::string>
. Por otro lado, struct MyClass
está definido por el usuario, porque no es un tipo definido en el estándar, por lo que puede especializar std::vector<MyClass>
.
Este no es el mismo significado de "definido por el usuario" utilizado en las cláusulas 1-16, y esa diferencia es confusa y tonta. Hay un informe de defectos para esto, con una discusión grabada que básicamente dice "sí, la biblioteca usa el término incorrecto, pero no tenemos uno mejor".
Entonces, la respuesta a su pregunta es "depende". Si está hablando con un implementador de compilador de C ++ o un experto en lenguaje central, std::vector<int>
es definitivamente un tipo definido por el usuario, pero si está hablando con un implementador de biblioteca estándar, no lo es. Más precisamente, no está definido por el usuario a los efectos de 17.6.4.2.1 .
Una forma de verlo es que la biblioteca estándar es "código de usuario" en lo que respecta al lenguaje central. Pero la biblioteca estándar tiene una idea diferente de "usuarios" y se considera parte de la implementación, y solo las cosas que no son parte de la biblioteca están "definidas por el usuario".
Editar: he propuesto cambiar las cláusulas de la biblioteca para usar un nuevo término, "definido por el programa", que significa algo definido en su programa (a diferencia de los UDT definidos en el estándar, como std::string
).
El Prof. Stroustrup es muy claro en que cualquier tipo que no esté incorporado está definido por el usuario . Consulte el segundo párrafo de la sección 9.1 en Principios de programación y práctica con C ++.
Incluso llama específicamente a "tipos de biblioteca estándar" como un ejemplo de tipos definidos por el usuario. En otras palabras, un tipo definido por el usuario es cualquier tipo compuesto.
El artículo menciona explícitamente que no todo el mundo parece estar de acuerdo, pero esto es en mi humilde opinión principalmente ilusiones y no lo que el estándar (y el Prof. Stroustrup) realmente dicen, solo lo que algunas personas quieren leer en él.
El borrador del estándar contrasta los tipos fundamentales con los tipos definidos por el usuario en un par de lugares (no normativos).
El borrador del estándar también usa el término "definido por el usuario" en otros contextos, refiriéndose a entidades creadas por el programador o definidas en la biblioteca estándar. Los ejemplos incluyen el constructor definido por el usuario, el operador definido por el usuario y la conversión definida por el usuario.
Estos hechos nos permiten, a falta de otra evidencia, suponer provisionalmente que la intención del estándar es que el tipo definido por el usuario debe significar el tipo compuesto, de acuerdo con el uso histórico. Solo una aclaración explícita en un futuro documento estándar definitivamente puede resolver el problema.
Tenga en cuenta que el uso histórico no es claro en tipos como int*
o struct foo*
o void(*)(struct foo****)
. Son compuestos, pero ¿deberían considerarse (o algunos de ellos) definidos por el usuario?