Espacios de nombres
Variantes
Acciones

Tipo

De cppreference.com
< cpp‎ | language

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:

  • 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 referencia a l-valor a tipos objeto;
  • los tipos referencia a l-valor a tipos función;
  • los tipos referencia a r-valor a tipos objeto;
  • los tipos referencia a r-valor a tipos función;

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:

[editar] Denominación de tipos

Un nombre puede declararse para referirse a un tipo mediante:

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:

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.

[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:

Todos los otros tipos son tipos completos.

Cualquiera de los siguientes contextos requieren que el tipo T esté completo:

(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