Almiar
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, el conversión de matriz a puntero no se llevaría a cabo.
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 un extra const
indicando que el puntero es un const
permitir que se compile?
StoryTeller – Unslander Mónica
Es esencialmente adherirse a esta fórmula.
T const & a = something_convertible_to_T;
donde esta t const char*
. En el primer caso, se puede materializar un puntero temporal, asignarle la dirección del literal y luego vincularlo a la referencia. En el segundo caso, dado que la referencia lvalue 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á vinculado a una referencia rvalue.
-
Sí, lo entiendo. Es la misma razón que para
const int& a = 3; //yes
,int&a = 3; //no
. los fórmula es muy útil Gracias. Simplemente se vuelve más difícil de reconocer cuando se trata de un puntero.– Rick
27 de agosto de 2018 a las 5:03
-
@SkepticalEmpiricist – Lo es. Lo que es importante tener en cuenta al tomar ese comentario es que la conversión de identidad también cuenta (aunque obviamente no es un caso muy interesante).
const int& a = 3;
materializa un valor temporal a partir de un valor int literal y vincula una referencia a él.– StoryTeller – Unslander Mónica
27 de agosto de 2018 a las 7:34
-
@StoryTeller Todavía no puedo seguir completamente, pensé que sabía lo que conversión de identidad significa en The Standard pero no puedo ponerlo en uso tratando de entender lo que estás diciendo. ¿Puedo pedirle amablemente que explique esto?
– Viejo
27 de agosto de 2018 a las 7:44
-
@SkepticalEmpiricist: no estaba tratando de ser tan exacto como el estándar en esto, pero mi línea de pensamiento (y explicación) intenta seguir los cambios de C ++ 17 a prvalues, el conversión de materialización temporal y esto sobre inicializar referencias.
– StoryTeller – Unslander Mónica
27 de agosto de 2018 a las 7:50
-
@SkepticalEmpiricist: es más simple que eso. el temporal pueden materializarse en todos los ejemplos. Es solo que la totalidad de [dcl.init.ref]/5 no define el significado de tal inicialización. Así que el programa está mal formado.
– StoryTeller – Unslander Mónica
27 de agosto de 2018 a las 8:17
Vejestorio
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.
Entonces, sintácticamente, en ambas líneas definimos un referencia a
y en ambos tendremos un materialización de un puntero temporal que toma la dirección del literal de cadena. La unica diferencia entre los dos es el 2do const
apareciendo solo aquí:
const char* const & a = "hello";
y no aquí:
const char*& a = "hello";
este 2do const
denota que el objeto siendo referenciado aquí, un puntero en este caso, es en sí mismo constya que no se puede modificar usando esta referencia.
Por lo tanto, porque el escribe de esta literal de cadena es const char[6]
(y no const char *
por ejemplo), nuestro lvalue
referencia al tipo const char*
en la segunda línea no puede vincularse a él, pero la referencia en la primera línea, siendo una referencia al tipo const char* const
pudo. ¿Por qué? debido a la reglas de inicialización de referencia:
(5) Una referencia al tipo “cv1 T1” se inicializa con una expresión de tipo “cv2 T2” de la siguiente manera:
(5.1) Si la referencia es una referencia lvalue y la expresión del inicializador
- (5.1.1) es un valor l (pero no es un campo de bits), y “cv1 T1” es compatible con referencias a “cv2 T2”, […]
- (5.1.2) tiene un tipo de clase (es decir, T2 es un tipo de clase) […]
Ambas expresiones son valorpero nuestro “cv1 T1” no es compatible con referencias con nuestro “cv2 T2” y “T2” no es un tipo de clase.
- (5.2) De lo contrario, si la referencia es un lvalue referencia a un tipo que no está calificado const o está calificado como volátil, el programa esta mal formado
La referencia es de hecho no const-calificado: nuestro “T1” es const char*
el cual es un puntero a const a diferencia de un puntero constante. El tipo real aquí es un tipo de puntero, por lo 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 de ser referencia de valor no constante es exactamente ¶5.2 — el valor 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 la vinculación a un tipo no relacionado es exactamente ¶5.1 — nuestro const char*
no es compatible siendo el RHS const char[6]
o const char* const
después matriz a puntero conversión.
Por esta razón exacta, o la falta de ella, esto puede compilar sin errores:
char* const & a = "hello";
ISOC++11 Dejando de lado la advertencia, un compilador deja pasar este (no es que deba hacerlo, ya que el literal de la cadena es un ‘const char 6‘ y no deberíamos dejar caer esto primero const
), ya que la referencia es ahora const
con respecto a su objeto, el puntero.
Otra cosa interesante es que An rvalue
referencia const char* && a
(sin “2do const
“) pudo enlazar con el puntero temporal que se ha materializado a partir de la literal de cadena, como lo proporcionó @StoryTeller. ¿Porqué es eso? Debido a las reglas de conversión de matriz a puntero:
Un valor o valor de tipo “matriz de NT” o “matriz de límite desconocido de T” se puede convertir en un prvalue de tipo “puntero a T”.
sin mención de const
u otro cv-cualificación expresando aquí, pero esto se mantiene solo mientras estemos inicializando un rvalue ref.
“al inicializar una referencia, la conversión de matriz a puntero no se llevaría a cabo”. Esto no es exactamente correcto. Al inicializar un referencia a una matriz, la conversión de matriz a puntero no se llevaría a cabo. No existe tal regla para inicializar una referencia a un puntero.
– norte 1.8e9-dónde-está-mi-participación m.
18 de septiembre de 2018 a las 5:51