valor usar sentencia referencia hace float definir define constantes como c++ string templates compile-time string-literals

usar - definir un valor en c++



¿Algunos caracteres const no están disponibles en tiempo de compilación? (2)

Supongamos que tenemos una función de plantilla con un parámetro no tipo de const char * como este:

template <const char * MESSAGE> void print() { std::cout << MESSAGE << ''/n''; }

Usar esta plantilla no sería un problema, ya que el registro ya que el MESSAGE se puede deducir en tiempo de compilación, por lo que los siguientes usos son legales:

namespace { char namespace_message[] = "Anonymous Namespace Message"; constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message"; } char message[] = "Message"; constexpr char constexpr_message[] = "Constexpr Message"; int main() { print<namespace_message>(); print<namespace_constexpr_message>(); print<message>(); print<constexpr_message>(); return 0; }

Pero los siguientes no son ( ver aquí ):

namespace { const char namespace_const_message[] = "Anonymous Namespace Const Message"; } const char const_message[] = "Const Message"; int main() { print<namespace_const_message>(); print<const_message>(); print<"Literal">(); return 0; }

Los errores generados por el código anterior son los siguientes:

el valor de ''{anonymous} :: namespace_const_message'' no se puede utilizar en una expresión constante

No entiendo por qué namespace_const_message no se puede usar en una expresión constante mientras que namespace_message es; si debo apostar para que uno de ellos no se pueda usar en una expresión constante, apostaré por la no constante, ¡pero es la que ya funciona como expresión constante!

nota: ''{anonymous} :: namespace_const_message'' no fue declarado ''constexpr''

namespace_message no se declaró como constexpr y se usa en una expresión constante y su valor se deduce en el momento de la compilación. ¿Por constexpr se necesita constexpr si la expresión es const y no se requiere si no-const?

Lo mismo ocurre con los valores fuera del espacio de nombres anónimo, estaba tratando de forzar la constancia de tiempo de compilación colocando los valores en un espacio de enlace interno, pero es obvio que he fallado.

Finalmente, el último error:

''"Literal"'' no es un argumento de plantilla válido para el tipo ''const char *'' porque los literales de cadena nunca se pueden usar en este contexto

Entonces, sorprendentemente (al menos fue una sorpresa para mí) un literal de cadena no se puede usar como argumento de plantilla, pero siempre que la cadena (bueno, un puntero a una matriz de caracteres terminada en nulo) sea un valor de tiempo de compilación se pueden usar como parámetros de plantilla que no son de tipo, por lo que están disponibles en tiempo de compilación siempre y cuando "sean un valor lime" (¡pero ya son valores lorales !).

Estoy tratando de adivinar por qué un literal de cadena nunca se puede usar en este contexto, y mi mejor conjetura es que dos literales de cadena con el mismo contenido no son el mismo literal (porque el puntero que apunta al contenido podría ser diferente) mientras que dos literales integrales son iguales (son un valor, no un puntero a un valor).

Entonces, ¿cuál es la pregunta aquí?

  • ¿Por qué namespace_const_message y const_message no están disponibles en tiempo de compilación y, por lo tanto, están prohibidos en la función de plantilla de print ?
  • ¿Mi conjetura sobre los literales de cadena es correcta?

Gracias.


De la norma c ++ 11 §14.3.2.1

Plantilla de argumentos no tipográficos

Un argumento de plantilla para un parámetro de plantilla que no sea de tipo ni de plantilla será uno de los siguientes:

  1. para un parámetro de plantilla de tipo no integral o de enumeración, una expresión constante convertida (5.19) del tipo de parámetro de plantilla; o
  2. el nombre de un parámetro-plantilla no-tipo; o
  3. 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 las plantillas de función y los identificadores de plantilla de función pero excluyendo miembros de clase no estáticos, expresados ​​(ignorando paréntesis) como & id-expresión, excepto que & puede omitirse si el nombre se refiere a una función o matriz y se omitirá si el parámetro de plantilla correspondiente es una referencia; o
  4. una expresión constante que se evalúa como un valor de puntero nulo (4.10); o
  5. una expresión constante que se evalúa como un valor de puntero de miembro nulo (4.11); o
  6. un puntero a miembro expresado como se describe en 5.3.1; o
  7. una expresión constante de dirección de tipo std :: nullptr_t.

A tus preguntas:

¿Por qué namespace_const_message y const_message no están disponibles en tiempo de compilación y, por lo tanto, están prohibidos en la función de plantilla de impresión?

Por eso existe constexpr . Se pueden usar donde se necesita una evaluación en tiempo de compilación , por lo tanto, están disponibles como argumentos de plantilla.

¿Mi conjetura sobre los literales de cadena es correcta?

Hay una nota sobre esto justo después de los argumentos:

Nota: un literal de cadena (2.14.5) no satisface los requisitos de ninguna de estas categorías y, por lo tanto, no es un argumento de plantilla aceptable.


La variable de creación de instancias de una plantilla necesitaba tener un enlace externo, y const era implícitamente un enlace interno. Así que tienes que escribir:

extern char const constMessage[] = "Const message";

(Otra alternativa sería que sea un miembro de clase estática. Los miembros de clase estática siempre tienen vínculos externos).

El caso de los literales de cadena es en cierto modo similar: su tipo es char const[] . Pero es aún peor: las instancias de plantilla (al menos las primeras) necesitan un nombre, y un literal de cadena no tiene uno. Aún más al punto, no se especifica si los literales de cadena idénticos son o no el mismo objeto, por lo que en lo siguiente:

template <char const* m> struct Toto { char const* f() const; }; Toto <"titi"> t1; Toto <"titi"> t2;

no se especificaría si t1 y t2 tenían el mismo tipo o no.