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:

  1. ¿Es cierta mi conclusión?
  2. Si es así, ¿por qué un puntero no puede ser una expresión constante?Si no, ¿por qué no se compilan los programas anteriores?
  3. ¿C++ 0x (C++ 11, por así decirlo) cambia algo?

¡Gracias por cualquier idea!

¿Fue útil?

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:

  1. En C++03, sólo las expresiones integrales pueden ser expresiones constantes.
  2. Porque la norma lo dice (naturalmente).
  3. 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 el constexpr palabra clave en lugar de nivel superior const.

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.
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top