Declaraciones
Las declaraciones introducen (o reintroducen) nombres en un programa de C++. Cada tipo de entidad se declara de manera diferente. Las definiciones son declaraciones que son suficientes para usar la entidad identificada por el nombre.
Una declaración es una de las siguientes:
- definición de función;
- declaración de plantilla (incluyendo la especialización parcial de plantilla);
- instanciación explícita de plantilla;
- especialización explícita de plantilla;
- definición de espacio de nombres;
- especificación de vinculación;
- declaración de atributos (atrib
;
); (desde C++11) - declaración vacía (
;
) (desde C++11) - declaración de función sin una sec-decl-especificadores:
declarador atrib(opcional) ;
|
|||||||||
atrib (desde C++11) | - | secuencia de cualquier número de atributos |
declarador | - | declarador de función. |
- Esta declaración tiene que declarar un constructor, destructor o una función de conversión de un tipo definido por el usuario. Solamente puede ser usada como parte de una declaración de plantilla, especialización explícita, o creación implícita.
- declaración-de-bloque (una declaración que puede aparecer dentro de un bloque), que a su vez puede ser uno de los siguientes:
- declaración asm;
- declaración de alias de tipo; (desde C++11)
- definición de alias de espacio de nombres;
- declaración
using
; - directiva
using
; - declaración
using enum
(desde C++20) - declaración de aserción estática (
static_assert
); (desde C++11) - declaración de enumeración opaca; (desde C++11)
- declaración simple.
Contenido |
[editar] Declaración simple
Una declaración simple es una instrucción que introduce, crea y opcionalmente inicializa uno o varios identificadores, típicamente variables.
sec-decl-espec lista-decl-inic(opcional) ;
|
(1) | ||||||||
atrib sec-decl-especificadores lista-decl-inic;
|
(2) | ||||||||
atrib (desde C++11) | - | Secuencia de cualquier número de atributos |
sec-decl-especificadores | - | Secuencia de especificadores (véase abajo). |
lista-decl-inic | - | Lista separada por comas de declaradores con inicializadores opcionales. La lista-decl-inic es opcional cuando se declara una class/struct/union con nombre o una enumeración con nombre. |
Una declaración de vínculos estructurados también es una declaración simple. (desde C++17)
[editar] Especificadores
Los especificadores de declaración (sec-decl-especificadores) es una secuencia de los siguientes especificadores separados por espacios en blanco, en cualquier orden:
- El especificador
typedef
. Si está presente, toda la declaración es una declaración typedef y cada declarador introduce un nuevo nombre de tipo, no un objeto ni una función. - Los especificadores de función (
inline
,virtual
yexplicit
) solamente se permiten en las declaraciones de función
|
(desde C++17) |
- El especificador
friend
. Se permite en declaraciones de clases y funciones.
|
(desde C++11) |
|
(desde C++20) |
- especificador de clase de almacenamiento (register, static, thread_local (desde C++11), extern, mutable). Solamente se permite un especificador de clase de almacenamiento, excepto que
thread_local
puede aparecer junto conextern
ostatic
. - Especificadores de tipo (sec-espec-de-tipo). Una secuencia de especificadores que dan nombre a un tipo. El tipo de cada entidad introducida por la declaración es este tipo, opcionalmente modificado por el declarador (véase abajo). Esta secuencia de especificadores también se usa por type-id. Solamente los siguientes especificadores son parte de la sec-especificadores-de-tipo, en cualquier orden:
- especificador class;
- especificador enum;
- especificador de tipo simple:
(desde C++11) |
- nombre de clase previamente declarado (opcionalmente calificado);
- nombre de enumeración previamente declarado (opcionalmente calificado);
- nombre de typedef previamente declarado o alias de tipo (desde C++11) (opcionalmente calificado);
- nombre de plantilla con argumentos de plantilla (opcionalmente calificado, opcionalmente usando un desambiguador de plantilla);
|
(desde C++17) |
-
- la palabra clave class, struct, o union, seguida de un identificador (opcionalmente calificado), previamente definido como el nombre de una clase, estructura o unión;
- la palabra clave class, struct, o union, seguida por el nombre de la plantilla con argumentos de plantilla (opcionalmente calificada, opcionalmente usando un desambiguador de plantilla), previamente definida como el nombre de una plantilla de clase;
- la palabra clave enum seguida del identificador (opcionalmente calificada), previamente declarada como el nombre de una enumeración;
- solamente se permite un especificador de tipo en una sec-decl-especificadores, con las siguientes excepciones:
- -
const
puede combinarse con cualquier especificador de tipo excepto consigo mismo; - -
volatile
puede combinarse con cualquier especificador de tipo excepto consigo mismo; - -
signed
ounsigned
pueden combinarse conchar
,long
,short
, oint
; - -
short
olong
pueden combinarse conint
; - -
long
puede combinarse condouble
;
|
(desde C++11) |
Los atributos pueden aparecer en una sec-decl-especificadores, y en tal caso se aplican al tipo determinado por los especificadores precedentes.
El único especificador que se permite que aparezca dos veces en una sec-decl-especificadores es |
(desde C++17) |
[editar] Declaradores
Una lista-declaradores-init es una secuencia separada por comas de uno o más declaradores-init, que tienen la siguiente sintaxis:
declarador inicializador(opcional) | (1) | ||||||||
declarador cláusula-requiere | (2) | (desde C++20) | |||||||
declarador | - | El declarador |
inicializador | - | Inicializador opcional (excepto donde se requiere, como al inicializar referencias u objetos constantes). Véase Inicialización para más detalles. |
cláusula-requiere(C++20) | - | Una cláusula-requiere, que agrega una constricción a una declaración de función |
Cada declarador-init en una secuencia declarador-init S D1, D2, D3; se procesa como si fuera una declaración independiente con los mismos especificadores: S D1; S D2; S D3;.
Cada declarador introduce exactamente un objeto, referencia, función o (para declaraciones typedef
) alias de tipo, cuyo tipo se provee por una sec-decl-especificadores y opcionalmente se modifica por operadores tales como & (una referencia a) o [] (un array de) o () (una función que devuelve) en el declarador. Estos operadores pueden aplicarse recursivamente, como se muestra posteriormente.
Un declarador es uno de los siguientes:
id-no-calificado atrib(opcional) | (1) | ||||||||
id-calificado atrib(opcional) | (2) | ||||||||
... identificador atrib(opcional)
|
(3) | (desde C++11) | |||||||
* atrib(opcional) cv(opcional) declarador
|
(4) | ||||||||
especificador-de-nombre-anidado * atrib(opcional) cv(opcional) declarador
|
(5) | ||||||||
& atrib(opcional) declarador
|
(6) | ||||||||
&& atrib(opcional) declarador
|
(7) | (desde C++11) | |||||||
declarador-noptr [ constexpr(opcional) ] atrib(opcional)
|
(8) | ||||||||
declarador-noptr ( lista-de-parámetros ) cv(opcional) ref(opcional) except(opcional) atrib(opcional)
|
(9) | ||||||||
D
como un puntero al tipo determinado por la sec-decl-especificadores S
.D
como un puntero a miembro de C
del tipo determinado por la sec-decl-especificadores S
. El especificador-de-nombre-anidado es una secuencia de nombres y operadores de resolución de ámbito ::
D
como una referencia lvalue al tipo determinado por la sec-decl-especificadores S
.D
como una referencia rvalue al tipo determinado por la sec-decl-especificadores S
.En todos los casos, atrib es una secuencia opcional de atributos. Cuando aparece inmediatamente después del identificador, se aplica al objeto que se está declarando.
cv es una secuencia de calificadores const y volatile, donde cualquiera de los calificadores puede aparecer a lo más una vez en la secuencia.
Esta sección está incompleta Razón: Explicar las reglas de declaraciones que ocultan nombres; como una declaración de variable o función esconde una clase (pero no un typedef ) con el mismo nombre. |
[editar] Notas
Cuando una declaración-de-bloque aparece dentro de un bloque, y u identificador introducido por una declaración fue declarado previamente en un bloque externo, la declaración externa se oculta durante el resto del bloque.
Si una declaración introduce una variable con duración de almacenamiento automático, la variable se inicializa cuando su instrucción de declaración se ejecuta. Todas las variables automáticas declaradas en un bloque se destruyen a la salida del bloque (independientemente de cómo se sale del bloque: mediante una excepción, goto, o al alcanzar su fin), en el orden opuesto de su orden de inicialización.
[editar] Entender las declaraciones de C
Para entender una declaración de C sigue estas reglas:
- Predecencia de alto a bajo:
- Los paréntesis se agrupan juntos como parte de la declaración.
- Operadores posfijos, tales como () (que indica una función) o [] (que indica un array).
- Operadores prefijos, tales como *, que indica un puntero.
- Si ocurre un calificador-cv (p. ej. const o volatile) a la derecha del asterisco * (p. ej. int * const) entonces el calificador-cv se aplica al puntero (p. ej. int * const es un puntero const a int). De otra manera, si un calificador-cf ocurre a la izquierda de un asterisco y enseguida de un especificador de tipo, entonces el calificador-cv se aplica al tipo (p. ej. const int * o int const * se refieren ambos a un puntero a const int).
Para entender una declaración de C tal como int const * const * (*p[])(float), hacemos lo siguiente:
- Empieza con el nombre de la declaración (p. ej. p) y di "p un/una...". Sigue las reglas de precedencia dadas anteriormente en dirección externa desde el nombre, y aplica lo siguiente:
- si a la derecha inmediata existen corchetes (p. ej. []) entonces di "un "array de...".
- si a la derecha inmediate existe un paréntesis abierto (lista-de-param) (p. ej. () o (float, int)) entonces di "una función con parámetros (lista-de-param) que devuelve...".
- si a la izquierda inmediate existe un asterisco (y posiblemente calificadores-cv) entonces di "puntero(s) a..." (p. ej. for int const * const di "puntero const a const int).
- Repite (2) - (5) como sea necesario.
En nuestro ejemplo int const * const * (*p[])(float), "p es un array (por 2.) de punteros (por 4.) a funciones con parámetros (float) que devuelven (por 3.) un puntero a un puntero a const a const int," que significa que cada función a la que se apunta tiene un prototipo int const * const * funcname(float).
[editar] Ejemplos
#include <string> class C { std::string member; // decl-specifier-seq is "std::string" // declarator is "member" } obj, *pObj(&obj); // decl-specifier-seq is "class C { std::string member; }" // declarator "obj" defines an object of type C // declarator "*pObj(&obj)" declares and initializes a pointer to C int a = 1, *p = nullptr, f(), (*pf)(double); // decl-specifier-seq is int // declarator a = 1 defines and initializes a variable of type int // declarator *p = nullptr defines and initializes a variable of type int* // declarator (f)() declares (but doesn't define) // a function taking no arguments and returning int // declarator (*pf)(double) defines a pointer to function // taking double and returning int int (*(*foo)(double))[3] = nullptr; // decl-specifier-seq is int // 1. declarator "(*(*foo)(double))[3]" is an array declarator: // the type declared is "/nested declarator/ array of 3 int" // 2. the nested declarator is "(*(*foo)(double))", which is a pointer declarator // the type declared is "/nested declarator/ pointer to array of 3 int" // 3. the nested declarator is "(*foo)(double)", which is a function declarator // the type declared is "/nested declarator/ function taking double and returning // pointer to array of 3 int" // 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by // function declarator syntax) pointer declarator. // the type declared is "/nested declarator/ pointer to function taking double // and returning pointer to array of 3 int" // 5. the nested declarator is "foo", which is an identifier. // The declaration declares the object foo of type "pointer to function taking double // and returning pointer to array of 3 int" // The initializer "= nullptr" provides the initial value of this pointer.