¿Por qué un puntero constante no puede ser una expresión constante?
-
29-10-2019 - |
Pregunta
El siguiente programa compila:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
int main()
{
Test<&var> test;
}
Este, sin embargo, no es así, lo cual es una sorpresa para mí:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
Test<ptr> test; //FAIL! Expected constant expression.
}
Ejemplo alternativo:
int main()
{
const int size = 42;
int ok[*&size]; //OK
const int * const pSize = &size;
int fail[*pSize]; //FAIL
}
He llegado a la conclusión de que un puntero simplemente no puede ser una expresión constante independientemente de si es constante y se inicializa con una expresión constante.
Preguntas:
- ¿Es cierta mi conclusión?
- Si es así, ¿por qué un puntero no puede ser una expresión constante?Si no, ¿por qué no se compilan los programas anteriores?
- ¿C++ 0x (C++ 11, por así decirlo) cambia algo?
¡Gracias por cualquier idea!
Solución
Es un poco más complicado.En C++03 y C++11, &var
es una expresión constante si var
es una variable de alcance de espacio de nombres o estática de clase/estática local.Esto se llama expresión constante de dirección.Se garantiza que la inicialización de una variable de puntero de alcance de espacio de nombres o clase estática con esa expresión constante se realizará antes de ejecutar cualquier código (fase de inicialización estática), debido a que es una expresión constante.
Sin embargo, sólo desde C++ 11, un constexpr variable de puntero que almacena la dirección &var
también se puede usar como una expresión constante de dirección y solo desde C++ 11, puede desreferenciar una expresión constante de dirección (en realidad, puede desreferenciar aún más, incluso direcciones de elementos de matriz locales, pero mantengámoslo en el tema) y si se refiere a una variable integral constante inicializada antes de la desreferencia o una variable constexpr, nuevamente obtiene una expresión constante (dependiendo del tipo y categoría de valor, el tipo de expresión constante puede variar).Como tal, lo siguiente es válido en C++11:
int const x = 42;
constexpr int const *px = &x;
// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }
Si es así, ¿por qué un puntero no puede ser una expresión constante?Si no, ¿por qué no se compilan los programas anteriores?
Esta es una limitación conocida en la redacción del Estándar: actualmente solo permite otros parámetros de plantilla como argumentos o & object
, para un parámetro de plantilla de tipo puntero.Aunque el compilador debería ser capaz de hacer mucho más.
Otros consejos
Todavía no está permitido en C++ 0x. temp.arg.nontype
requiere:
Un argumento de plantilla para un parámetro de plantilla que no sea de tipo ni de plantilla será uno de los siguientes:
- para un parámetro de plantilla que no es de tipo de tipo integral o de enumeración, una expresión constante convertida (5.19) del tipo del parámetro de plantilla;o
- el nombre de un parámetro de plantilla que no es de tipo;o
- Una expresión constante (5.19) que designa la dirección de un objeto con duración de almacenamiento estático y enlace externo o interno o una función con enlace externo o interno, incluidas plantillas de función e ID de plantilla de función, pero excluyendo miembros de clase no estática, expresado (ignorando paréntesis) como
&
id-expression
, excepto que el & puede omitirse si el nombre se refiere a una función o matriz y se omitirá si la plantilla correspondiente es una referencia;o- una expresión constante que se evalúa como un valor de puntero nulo (4.10);o
- una expresión constante que se evalúa como un valor de puntero de miembro nulo (4.11);o
- un puntero a miembro expresado como se describe en 5.3.1.
respuesta original:
- En C++03, sólo las expresiones integrales pueden ser expresiones constantes.
- Porque la norma lo dice (naturalmente).
- En C++ 0x, n3290 incluye ejemplos usando
constexpr
en un puntero.Entonces lo que estás intentando ahora debería ser posible, aunque ahora debes usar elconstexpr
palabra clave en lugar de nivel superiorconst
.
También hay un error de gcc involucrado, g++ rechaza los propios ejemplos de validez del borrador estándar. constexpr
uso.
El problema se debe a que su programa C++ se puede cargar en cualquier punto de la memoria, por lo que la dirección de un archivo global var
puede ser diferente cada vez que ejecuta el programa.¿Qué sucede si ejecuta su programa dos veces? var
Obviamente está en dos ubicaciones diferentes.
¡Peor aún, en tu ejemplo, tomas la dirección de una variable en la pila!mira este:
void myfunction( unsigned int depth) {
const int myvar = depth;
const int * const myptr = &myvar;
if (depth)
myfunction(depth-1);
}
Si main llama a myfunction(3), entonces se crean 3 myvars en ubicaciones separadas.No hay manera de que el tiempo de compilación sepa siquiera cómo muchos Se crean myvars, y mucho menos hay ubicaciones exactas.
Finalmente:declarar una variable como const
medio:"Lo prometo", y lo hace no significa que es una constante de tiempo de compilación.Vea este ejemplo:
int main(int argc, char** argv) {
const int cargc = argc;
char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}