Tipo
Los objetos, referencias, funciones (incluyendo las funciones de plantilla especializadas), y las expresiones tienen una propiedad denominada “tipo”, que restringe las operaciones permitidas para esas entidades y proporciona un significado semántico a las secuencias de bits que de otro modo serían genéricas.
Contenido |
[editar] Clasificación de tipos
El sistema de tipos en C++ consiste en los siguientes tipos:
- los tipos fundamentales (véase también std::is_fundamental):
- el tipo void (véase también std::is_void);
- el tipo std::nullptr_t (desde C++11) (véase también std::is_null_pointer);
- los tipos aritméticos (véase también std::is_arithmetic):
- los tipos de punto flotante (float, double, long double y sus versiones calificadas-cv) (véase también std::is_floating_point);
- los tipos enteros (incluyendo las versiones calificadas-cv, véase también std::is_integral):
- el tipo bool;
- los tipos carácter:
- los tipos carácter estrechos:
- los tipos carácter ordinarios (char, signed char, unsigned char)
- el tipo char8_t (desde C++20)
- los tipos carácter anchos (char16_t (desde C++11), char32_t (desde C++11), wchar_t);
- los tipos enteros con signo (short int, int, long int, long long int);
- los tipos enteros sin signo (unsigned short int, unsigned int, unsigned long int, unsigned long long int);
- los tipos compuestos (véase también std::is_compound):
- los tipos referencia (véase también std::is_reference):
- los tipos referencia a l-valor (véase también std::is_lvalue_reference):
- los tipos referencia a l-valor a tipos objeto;
- los tipos referencia a l-valor a tipos función;
- los tipos referencia a r-valor (véase también std::is_rvalue_reference):
- los tipos referencia a r-valor a tipos objeto;
- los tipos referencia a r-valor a tipos función;
- los tipos puntero (véase también std::is_pointer):
- los tipos puntero a miembro ( véase también std::is_member_pointer):
- los tipos puntero a dato miembro (véase también std::is_member_object_pointer);
- los tipos puntero a función miembro (véase también std::is_member_function_pointer);
- los tipos array (véase también std::is_array);
- los tipos función (véase también std::is_function);
- los tipos enumeración (véase también std::is_enum);
- los tipos clase:
- los tipos no-unión (véase también std::is_class);
- los tipos unión (véase también std::is_union).
Para cada tipo aparte de una referencia o función, el sistema de tipos admite además tres versiones calificadas-cv de dicho tipo: (const, volatile y const volatile).
Los tipos se agrupan en varias categorías según sus propiedades:
- los tipos objeto son tipos (posiblemente calificados-cv) que no son ni tipos función, ni tipos referencia, ni el tipo void (véase también std::is_object);
- los tipos escalares son tipos objeto ((posiblemente calificados-cv) que no son ni tipos array, ni tipos clase (véase también std::is_scalar);
- los tipos triviales (véase también std::is_trivial), tipos POD (compatible con lenguaje C) (véase también std::is_pod), tipos literal (véase también std::is_literal_type), y otras categorías referidas en la biblioteca de rasgos de tipo o como requerimientos denominados de tipo .
[editar] Denominación de tipos
Un nombre puede declararse para referirse a un tipo mediante:
- la declaración de una clase;
- la declaración de una unión;
- la declaración de una enumeración;
- la declaración una definición de tipo typedef;
- la declaración de un alias de tipo.
A menudo se necesita usar tipos que no tienen nombre en programas de C++; la sintaxis para esto se conoce como id-de-tipo. La sintaxis del id-de-tipo que nombra al tipo T es exactamente la sintaxis de una declaración de variable o función de tipo T, omitiendo el identificador, excepto que la secuencia-de-declaración-de-especificadores de la gramática de declaración está restringida a una secuencia-de-especificadores-de-tipo, y que los nuevos tipos pueden definirse solo si el id-de-tipo aparece en el lado derecho de una declaración de un alias que no sea de plantilla.
int* p; // declaración de un puntero a entero static_cast<int*>(p); // el id-de-tipo es "int*" int a[3]; // declaración de un array de 3 enteros new int[3]; // id-de-tipo es "int[3]" (denominado nuevo-id-de-tipo) int (*(*x[2])())[3]; // declaración de un array de 2 punteros a función // que devuelven un puntero a un array de 3 enteros new (int (*(*[2])())[3]); // id-de-tipo es "int (*(*[2])())[3]" void f(int); // declaración de una función que toma un entero y devuelve void std::function<void(int)> x = f; // el tipo del parámetro de plantilla tiene id-de-tipo "void(int)" std::function<auto(int) -> void> y = f; // igual std::vector<int> v; // declaración de un vector de enteros sizeof(std::vector<int>); // id-de-tipo es "std::vector<int>" struct { int x; } b; // crea nuevo tipo y declara un objeto b de ese tipo sizeof(struct{ int x; }); // ERROR: no se pueden definir nuevos tipos en una expresión sizeof using t = struct { int x; }; // crea nuevo tipo y declara t como un alias de ese tipo sizeof(static int); // ERROR: los especificadores de tipo de almacenamiento no son parte // de la secuencia de especificadores de tipo std::function<inline void(int)> f; // ERROR: tampoco lo son los especificadores de función
La parte del declarador de la gramática de declaración con el nombre eliminado se conoce como declarador-abstracto.
El id-de-tipo se puede utilizar en las siguientes situaciones:
- para especificar el tipo del destino en una expresión de conversión;
- como argumento para sizeof, alignof, alignas, new, y typeid;
- en el lado derecho de una declaración de alias de tipo;
- como el tipo de retorno al final en la declaración de función;
- como argumento por defecto de un tipo de parámetro de plantilla;
- como el argumento de plantilla para un tipo de parámetro de plantilla;
- en la especificación de excepción dinámica.
El “id-de-tipo” se puede usar con algunas modificaciones en los siguientes casos:
- en la lista de parámetros de una función (cuando se omite el nombre del parámetro), en su lugar el id-de-tipo usa la secuencia-de-declaración-de-especificadores en vez de una secuencia-de-especificadores-de-tipo (en particular, algunos especificadores de almacenamiento se permiten);
- en el nombre de una función de conversión definida por el usuario, el declarador abstracto no puede incluir operadores de array o de función.
Esta sección está incompleta Razón: 8.2[dcl.ambig.res] si se puede sumarizar compactadamente |
Esta sección está incompleta Razón: mencionar y enlazar a decltype y auto |
[editar] Especificador de tipo elaborado
Los especificadores de tipo elaborados se pueden usar para referirse a un nombre de clase (class
, struct
o union
) declarado previamente, o a un nombre de enumeración declarado previamente, incluso si el nombre estaba oculto por una declaración de no-tipo. También pueden usarse para declarar nuevos nombres de clase.
Véase especificador de tipo elaborado para más detalles.
[editar] Tipo estático
Al tipo de una expresión que resulta del análisis en tiempo de compilación del programa se le conoce como tipo estático. El tipo estático no cambia durante la ejecución del programa.
[editar] Tipo dinámico
Si alguna expresión gl-valor se refiere a un objeto polimórfico, al tipo de su objeto más derivado se le conoce como tipo dinámico.
// dado struct B { virtual ~B() {} }; // tipo polimórfico struct D: B {}; // tipo polimórfico D d; // objeto derivado B* ptr = &d; // el tipo estático de (*ptr) es B // el tipo dinámico de (*ptr) es D
Para las expresiones pr-valor, el tipo dinámico siempre es el mismo que el estático.
[editar] Tipo incompleto
Los siguientes son tipos incompletos:
- el tipo void (quizás calificado-cv);
- tipos objeto definidos de forma incompleta:
- un tipo clase que se declaró (p.ej., mediante una declaración adelantada) pero no está definido;
- un array de límite desconocido;
- un array de elementos de tipo incompleto;
- un tipo enumeración desde el punto de declaración hasta que se determina su tipo subyacente.
Todos los otros tipos son tipos completos.
Cualquiera de los siguientes contextos requieren que el tipo T
esté completo:
- una definición o llamada a una función con tipo de retorno
T
o tipo de argumentoT
; - la definición de un objeto de tipo
T
; - la declaración de un dato miembro no estático de una clase de tipo
T
; - la expresión-new para un objeto de tipo
T
o un array cuyos elementos son de tipoT
; - conversión de l-valor a r-valor aplicada a un gl-valor de tipo
T
; - la conversión implícita o explícita al tipo
T
; - la conversión estándar, conversión dinámica, o conversión estática al tipo
T*
oT&
, excepto al convertir de la constante puntero nulo o puntero avoid
; - un operador de acceso a miembro de clase aplicado a una expresión de tipo
T
; - un operador typeid, sizeof, o alignof aplicado al tipo
T
; - un operador aritmético aplicado a un puntero a
T
; - la definición de una clase con clase base
T
; - la asignación a un l-valor de tipo
T
; - una cláusula
catch
para una excepción de tipoT
,T&
oT*
.
(En general, cuando se debe conocer el tamaño y formato de T
).
Si alguna de estas situaciones se da en una unidad de traducción, la definición del tipo debe aparecer en la misma. De lo contrario, no se requiere.
Se puede completar un tipo de objeto definido de forma incompleta:
- Un tipo clase (como class X) puede estar incompleto en un punto de una unidad de traducción y completarse más adelante; el tipo class X es el mismo tipo en ambos puntos:
struct X; // X es un tipo incompleto extern X* xp; // xp es un puntero a un tipo incompleto void foo() { xp++; // mal formado: X está incompleto } struct X { int i; }; // ahora X es un tipo completo X x; void bar() { xp = &x; // de acuerdo: el tipo es “puntero a X” xp++; // de acuerdo: X está completo }
- El tipo declarado de un objeto de tipo array puede ser un array de tipo clase incompleto y, por lo tanto, incompleto; si el tipo clase se completa más adelante en la unidad de traducción, el tipo array se completa; el tipo array en esos dos puntos es el mismo tipo.
- El tipo declarado de un objeto de tipo array puede ser un array de límite desconocido y, por lo tanto, estar incompleto en un punto de una unidad de traducción y completarse más adelante; los tipos array en esos dos puntos ("array de límite desconocido de T" y "array de N T") son tipos diferentes.
El tipo de un puntero a un array de límite desconocido, o a un tipo definido por una declaración typedef como un array de límite desconocido, no se puede completar.
extern int arr[]; // el tipo de arr está incompleto typedef int UNKA[]; // UNKA es un tipo incompleto UNKA* arrp; // arrp es un puntero a un tipo incompleto UNKA** arrpp; void foo() { arrp++; // ERROR: tipo incompleto arrpp++; // de acuerdo: sizeof UNKA* se conoce } int arr[10]; // ahora el tipo de arr está completo void bar() { arrp = &arr; // ERROR: tipos distintos arrp++; // ERROR: UNKA no se puede completar }
[editar] Véase también
Documentación de C para Tipo
|