c++ reference const language-lawyer

c++ - ¿Por qué puede const char*const &="hello" compilar?



reference language-lawyer (2)

Estoy leyendo un fragmento de código de un libro y encuentro esto:

const char* const & a = "hello"; //can compile const char*& a = "hello"; //cannot

Todo lo que sé es que al inicializar una referencia, la conversión de matriz a puntero no se realizará.

const char* const & , una referencia a un const pointer , el puntero apunta a const char .

const char*& , una referencia a un pointer , el puntero apunta a const char .

Entonces, ¿por qué agregar una const adicional, lo que indica que el puntero es una const , le permite compilar?


Algunas frases adicionales para los aburridos, después de leer la excelente respuesta de @StoryTeller, ya que tuve que pasar por un proceso de pensamiento diferente sobre esto.

De manera sintáctica, en ambas líneas definimos una referencia a , y en ambas tendremos una materialización de un puntero temporal que toma la dirección de la cadena literal . La única diferencia entre los dos es que la segunda const aparece solo aquí:

const char* const & a = "hello";

y no aquí

const char*& a = "hello";

Esta segunda const denota que el objeto al que se hace referencia aquí, un puntero en este caso, es en sí mismo constante , ya que no puede modificarse utilizando esta referencia.

Por lo tanto, debido a que el tipo de esta cadena literal es const char[6] (y no const char * por ejemplo), nuestra referencia lvalue al tipo const char* en la segunda línea no puede vincularse a ella, pero la referencia en la primera línea , al ser una referencia al tipo const char* const podría. ¿Por qué? Por las reglas de inicialización de referencia :

(5) Una referencia al tipo "cv1 T1" se inicializa mediante una expresión del tipo "cv2 T2" de la siguiente manera:

  • (5.1) Si la referencia es una referencia de lvalor y la expresión de inicializador

    • (5.1.1) es un lvalue (pero no es un campo de bits), y "cv1 T1" es compatible con referencia con "cv2 T2", [...]
    • (5.1.2) tiene un tipo de clase (es decir, T2 es un tipo de clase) [...]

Ambas expresiones son lvalue s, pero nuestro "cv1 T1" no es reference-compatible con la reference-compatible con nuestro "cv2 T2" y "T2" no es un tipo de clase.

  • (5.2) De lo contrario, si la referencia es una referencia de lvalor a un tipo que no está cualificado constantemente o que está calificado como volátil, el programa está mal formado.

De hecho, la referencia no está constante: nuestro "T1" es const char* , que es un puntero a const , a diferencia de un puntero const . El tipo real aquí es un tipo de puntero, así que esto es lo que importa.

El error de Clang para la segunda línea, leído con eso en mente, nos dice exactamente esto:

error: non-const lvalue reference to type ''const char *'' cannot bind to a value of unrelated type ''const char [6]'' const char*& a = "hello"; ^ ~~~~~~~

La parte acerca de ser una referencia de valor no const es exactamente ¶5.2 - el valor de l en nuestro caso es un puntero a const , ¡pero en sí mismo no es const! A diferencia de la primera línea. La parte sobre el enlace a un tipo no relacionado es exactamente ¶5.1: nuestro const char* no es compatible con el RHS que es const char[6] , o const char* const después de la conversión de matriz a puntero .

Por esta razón exacta, o falta de ella, esto puede compilar sin errores:

char* const & a = "hello";

Dejando de lado la advertencia de ISO C ++ 11 , un compilador deja pasar esto (no es que deba hacerlo, ya que la cadena literal es un ''const char 6 '' y no deberíamos dejar caer esta primera const ), ya que la referencia ahora es const con Respecto a su objeto, el puntero.

Otra cosa interesante es que Un rvalue reference const char* && a (no "2nd const ") podría vincularse al puntero temporal que se ha materializado a partir de la cadena literal , como @StoryTeller proporcionó. ¿Porqué es eso? Debido a las reglas de la 6

Un lvalue o rvalue de tipo "matriz de NT" o "matriz de límite desconocido de T" se puede convertir en un prvalor de tipo "puntero a T" .

No hay mención de const u otra frase de calificación CV aquí, pero esto se mantiene solo mientras estemos inicializando un valor de referencia.


Es esencialmente adherirse a esta fórmula.

T const & a = something_convertible_to_T;

Donde T es const char* . En el primer caso, un puntero temporal se puede materializar, asignar la dirección del literal y luego vincularse a la referencia. En el segundo caso, dado que la referencia de valor l no es constante, no puede suceder. Otro ejemplo de más de lo mismo.

const char* && a = "hello"; // rvalue ref makes a no into a yes.

Ahora el puntero temporal está enlazado a una referencia rvalue.