c++ - resueltos - ¿Por qué un puntero constante no puede ser una expresión constante?
punteros y matrices en c (3)
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, 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 const o si se inicializa con una expresión constante.
Preguntas:
- ¿Es verdad mi conclusión?
- Si es así, ¿por qué un puntero no puede ser una expresión constante? Si no, ¿por qué los programas anteriores no se compilan?
- ¿C ++ 0x (C ++ 11, si se quiere) cambia algo?
Gracias por cualquier idea!
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 una var
global puede ser diferente cada vez que ejecuta el programa. ¿Qué sucede si ejecuta su programa dos veces? var
es obviamente en dos lugares diferentes entonces.
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 la llamada principal es myfunction (3), entonces se crean 3 myvars en ubicaciones separadas. No hay forma de que el tiempo de compilación ni siquiera sepa cuántos myvars se crean, y mucho menos las ubicaciones exactas.
Finalmente: declarar que una variable es const
significa: "Prometo", y no significa que sea una constante de tiempo de compilación. Mira 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.
}
Es un poco más complicado. En C ++ 03 y C ++ 11, &var
es una expresión constante si var
es una variable estática local / clase estática o de ámbito de espacio de nombres. Esto se llama una expresión constante de dirección. Se garantiza que la inicialización de una variable de puntero de ámbito estático o de espacio de nombre de clase 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, solo en C ++ 11, una variable de puntero constexpr que almacena la dirección &var
también se puede usar como expresión de dirección constante y solo en C ++ 11, puede desreferenciar una expresión constante de dirección (en realidad, puede desviar la referencia aún más) incluso direcciones de elemento de matriz local, pero mantengamos ontópico) y si se refiere a una variable integral constante inicializada antes de la desreferencia o una variable constexpr, de nuevo se obtiene una expresión constante (dependiendo del tipo y categoría de valor, el tipo de constante) la expresión puede variar). Como tal, el siguiente es válido 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é los programas anteriores no se compilan?
Esta es una limitación conocida en la redacción de la Norma: actualmente solo permite otros parámetros de plantilla como argumentos y & object
, para un parámetro de plantilla de tipo de puntero. Aunque el compilador debería ser capaz de hacer mucho más.
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 y sin plantilla será uno de:
- para un parámetro de plantilla sin tipo de tipo integral o de enumeración, una expresión de constante convertida (5.19) del tipo del parámetro de plantilla; o
- el nombre de un parámetro de plantilla sin tipo; o
- una expresión constante (5.19) que designa la dirección de un objeto con una duración de almacenamiento estática y un enlace externo o interno o una función con enlace externo o interno, incluyendo plantillas de función e id. de plantilla de función pero excluyendo miembros de clase no estáticos, expresados (ignorando paréntesis) como
&
id-expression
, excepto que & puede omitirse si el nombre se refiere a una función o matriz y debe omitirse si el parámetro-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 evalúa a un valor de puntero de miembro nulo (4.11); o
- un puntero al miembro expresado como se describe en 5.3.1.
respuesta original:
- En C ++ 03, solo las expresiones integrales pueden ser expresiones constantes.
- Porque el estándar lo dice (naturalmente).
- En C ++ 0x, n3290 incluye ejemplos que usan
constexpr
en un puntero. Entonces, lo que está intentando ahora debería ser posible, aunque ahora debe usar la palabra claveconstexpr
lugar deconst
nivel superior.
También hay un error de gcc involucrado, g ++ rechaza los ejemplos propios del borrador estándar de uso de constexpr
.