c++ - valores - que es una variable en programacion
¿Por qué no podemos declarar una variable de tipo nula? (7)
"tipo vacío es un tipo incompleto"
No puedes crear variables de ningún tipo incompleto
"... eso no se puede completar"
Si bien su ejemplo de estructura extern
incompleta puede completarse en algún momento posterior, el compilador sabe que ninguna declaración de tipo void
puede completarse.
Estoy buscando una explicación formal de ese hecho en el Estándar. He encontrado lo que dice 3.9.1 / 9 y tratando de dar una explicación usé esa sección.
Sección 3.9.1 / 9, N3797 :
El tipo vacío tiene un conjunto de valores vacío. El tipo de vacío es un tipo incompleto que no se puede completar. Se utiliza como el tipo de devolución para las funciones que no devuelven un valor. Cualquier expresión se puede convertir explícitamente a tipo cv void (5.4). Una expresión de tipo void se usará solo como un enunciado de expresión (6.2), como un operando de una expresión de coma (5.18), como un segundo o tercer operando de?: (5.16), como el operando de typeid, no except, o decltype, como la expresión en una declaración return (6.6.3) para una función con el tipo de retorno void, o como el operando de una conversión explícita a type cv void.
No entiendo cómo implica el hecho de que el tipo vacío tiene un conjunto vacío de valores?
Supongamos que el tipo T tiene un conjunto de valores vacío. ¿Por qué el compilador arroja un error cuando se encuentra con la siguiente línea?
extern T v;
Podemos descifrar una variable de tipo incompleto de la siguiente manera:
#include <iostream>
#include <cstring>
using namespace std;
struct Foo;
extern Foo f; //OK!
int main()
{
}
y funciona bien
No se puede hacer en un tipo vacío
#include <iostream>
#include <cstring>
using namespace std;
extern void f; //compile-time error
int main()
{
}
Bueno, realmente no veo la razón detrás de esto. Va a ser genial si de esta manera podemos declarar una variable con un tipo desconocido. Algo así como ''void *'' y arreglos de tamaño desconocido. Imagina un código como este:
#include <iostream>
#include <cstring>
using namespace std;
extern void f;
int main()
{
cout << (int &)f << endl; //cout ''f'' as it was integer
}
struct {
int a;
double b;
} f{};
Ahora puedes hacer algo similar con las matrices:
#include <iostream>
#include <cstring>
using namespace std;
struct Foo;
extern int arr[];
int main()
{
cout << arr[2] << endl;
}
int arr[4]{};
example vida
Debido a que C y C ++ suponen que cualquier objeto puede compararse para la identidad comparando sus direcciones, deben asegurarse de que todos los objetos tengan un tamaño no-cero fijo. Si no fuera por ese requisito, de hecho hay muchos casos en los que sería útil declarar objetos de tamaño cero [por ejemplo, en un código que usa plantillas que contienen campos que a veces serán útiles y otras no, o como un medio de forzar una estructura que debe rellenarse para cierta alineación que requiere que contenga un elemento que requiera dicha alineación]. Sin embargo, como es, los tipos de tamaño cero serían inconsistentes con el hecho de que la regla que especifica que cada objeto tiene una dirección única no incluye ninguna excepción que permitiría la existencia de objetos de tamaño cero que podrían compartir una dirección.
Incluso si se permitieran objetos de tamaño cero, sin embargo, un "puntero a objeto desconocido" no debería ser lo mismo que un "puntero a un objeto de tamaño cero". Dado que el tipo void*
se usa para el primero, eso implicaría que se debería usar algo más para este último, lo que a su vez implicaría que algo distinto del void
debería ser el tipo de cosa que señala un objeto de tamaño cero.
No puede declarar una variable de tipo void
porque las variables deben tener un tipo de objeto o ser referencias, extern void f;
no declara una referencia, y void
no es un tipo de objeto :
La sección 3 [basic]
dice que
Una variable es introducida por la declaración de una referencia que no sea un miembro de datos no estático o de un objeto.
La Sección 3.9 [basic.types]
dice que
Un tipo de objeto es un tipo (posiblemente calificado para CV ) que no es un tipo de función, no es un tipo de referencia y no es un tipo
void
.
Si la variable tiene un conjunto de valores vacío, no se puede usar para nada.
No puede asignarlo, porque no hay valores posibles para asignar.
No puede acceder a él, porque nunca lo ha asignado, por lo que tiene un valor indeterminado.
Como no hay valores posibles, no hay tamaño de la variable.
void
se acaba de usar como un marcador de posición en lugares variables. Se usa como un tipo de devolución para indicar que la función no devuelve un valor. Se usa en C
en la lista de argumentos para indicar que la función no toma argumentos (para resolver una ambigüedad de la versión previa al prototipo del lenguaje). Y se usa con declaraciones de puntero para crear punteros genéricos que se pueden traducir a cualquier otro tipo de puntero. No hay tal uso análogo en declaraciones variables.
void
es un tipo incompleto : solo puede declararles punteros y usarlos en las firmas de función. Obviamente, extern Foo f;
está permitido porque struct Foo
se puede definir en otra unidad de compilación (y si no es así, el linker detectará el error), pero void
no puede ser "definido" (y el compilador lo sabe, por supuesto) tan void
'' Es bastante especial en este caso.
[editar] La respuesta a continuación hace observaciones válidas, pero son contradictorias. Como estos pueden ser valiosos, no los eliminaré, pero vea la respuesta de Ben Voight y los comentarios allí para un enfoque más directo.
Sus observaciones sobre declaraciones extern
están específicamente permitidas por 7.1.1 / 8:
El nombre de una clase declarada pero indefinida se puede usar en una declaración externa. Dicha declaración solo se puede usar de forma que no requiera un tipo de clase completo.
void
no es una "clase declarada pero indefinida", y no hay otra excepción en 7.1.1 que se aplique.
Además, 3.9 / 5 es bastante explícito de que, de hecho, está permitido:
Una clase que ha sido declarada pero no definida , un tipo de enumeración en ciertos contextos (7.2), o una matriz de tamaño desconocido o de tipo de elemento incompleto, es un tipo de objeto definido de forma incompleta. [45] Los tipos de objetos incompletamente definidos y los tipos de espacios vacíos son tipos incompletos (3.9.1). Los objetos no se definirán para tener un tipo incompleto.
Énfasis mío Esta parte del estándar es bastante específica acerca de las diferencias entre las definiciones y las declaraciones, por lo que, por omisión, especifica que las declaraciones están permitidas.