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.