c++ pointers const const-correctness

c++ - ¿Por qué no es legal convertir "puntero a puntero a no const" a "puntero a puntero a const"?



pointers const-correctness (5)

Aquí hay dos reglas para notar:

  • No hay moldes implícitos entre T* y U* si T y U son tipos diferentes.
  • Puede convertir T* a T const * implícitamente. ("puntero a T" se puede convertir a "puntero a const T"). En C ++, si T también es un puntero, esta regla también se puede aplicar a él (encadenamiento).

Así por ejemplo:

char** significa: puntero a puntero a char .

Y const char** significa: puntero a puntero para const char .

Como el puntero a char y el puntero a const char son tipos diferentes que no se diferencian solo en const-ness, por lo que el molde no está permitido. El tipo correcto para transmitir debe ser const pointer a char .

Para que permanezca correcto, debe agregar la palabra clave const comenzando desde el asterisco situado más a la derecha.

Así que char** se puede convertir a char * const * y también se puede convertir a const char * const * .

Este encadenamiento es solo C ++. En C, este encadenamiento no funciona, por lo que en ese idioma no se pueden crear más de un nivel de punteros correctamente.

Es legal convertir un puntero-a-no-const a un puntero-a-const.

Entonces, ¿por qué no es legal convertir un puntero a puntero a no const a un puntero a puntero a const ?

Por ejemplo, ¿por qué el siguiente código es ilegal?

char *s1 = 0; const char *s2 = s1; // OK... char *a[MAX]; // aka char ** const char **ps = a; // error!


Del estándar:

const char c = ''c''; char* pc; const char** pcc = &pc; // not allowed *pcc = &c; *pc = ''C''; // would allow to modify a const object


El borrador del estándar C ++ 11 explica esto en una nota en la sección 4.4 que dice:

[Nota: si un programa pudiera asignar un puntero de tipo T ** a un puntero de tipo const T ** (es decir, si se permitiera la línea # 1 a continuación), un programa podría modificar inadvertidamente un objeto const (como está hecho) en la línea n. ° 2). Por ejemplo,

int main() { const char c = ''c''; char* pc; const char** pcc = &pc; // #1: not allowed *pcc = &c; *pc = ''C''; // #2: modifies a const object }

-Finalizar nota]

Se da una pregunta relacionada interesante int ** p1 y const int ** p2 es p1 == p2 bien formado? .

Tenga en cuenta que las preguntas frecuentes de C ++ también tienen una explicación para esto, pero me gusta más la explicación del estándar.

El texto conforme que acompaña a la nota es el siguiente:

Una conversión puede agregar cv-calificadores en niveles distintos al primero en punteros multinivel, sujeto a las siguientes reglas: 56

Los dos tipos de punteros T1 y T2 son similares si existe un tipo T y un entero n> 0 de modo que:

T1 es cv1.0 apuntador a cv1,1 apuntador a · · · cv1, n-1 apuntador a cv1, n T

y

T2 es un puntero de cv2,0 a un puntero de cv2,1 a · · · cv2, un puntero de n-1 a cv2, n T

donde cada cvi, j es const, volátil, const volátil o nada. La n-tupla de cv-calificadores después de la primera en un tipo de puntero, por ejemplo, cv1,1, cv1,2, · · ·, cv1, n en el puntero tipo T1, se denomina firma de cv-calificación del tipo de puntero . Una expresión de tipo T1 se puede convertir a tipo T2 si y solo si se cumplen las siguientes condiciones:

  • los tipos de puntero son similares.
  • para cada j> 0, si const está en cv1, j entonces const está en cv2, j, y de manera similar para volátil.
  • si cv1, j y cv2, j son diferentes, entonces const está en cada cv2, k para 0 <k <j.

Ignorando su código y respondiendo el principio de su pregunta, consulte esta entrada de comp.lang.c. FAQ: ¿Por qué no puedo pasar un char ** a una función que espera un const char **?

La razón por la que no puede asignar un valor de char ** a un puntero const char ** es algo oscura. Dado que el calificador const existe, el compilador desea ayudarlo a cumplir sus promesas de no modificar los valores de const . Es por eso que puedes asignar un char * a un const char * , pero no al revés: es claramente seguro "agregar" constness a un simple puntero, pero sería peligroso quitárselo. Sin embargo, suponga que realizó las siguientes series de asignaciones más complicadas:

const char c = ''x''; /* 1 */ char *p1; /* 2 */ const char **p2 = &p1; /* 3 */ *p2 = &c; /* 4 */ *p1 = ''X''; /* 5 */

En la línea 3, asignamos un char ** a un const char ** . (El compilador debería quejarse). En la línea 4, asignamos un const char * a un const char * ; esto es claramente legal. En la línea 5, modificamos a qué apunta un char * : se supone que es legal. Sin embargo, p1 termina apuntando a c , que es const . Esto sucedió en la línea 4, porque *p2 era realmente p1 . Esto se configuró en la línea 3, que es una asignación de un formulario que no se permite, y esta es exactamente la razón por la cual no se permite la línea 3.

Y como tu pregunta está etiquetada como C ++ y no como C, incluso explica qué calificadores const debes usar:

(C ++ tiene reglas más complicadas para asignar punteros calificados que le permiten realizar más tipos de asignaciones sin incurrir en advertencias, pero aún proteger contra intentos inadvertidos de modificar los valores de const. C ++ aún no permite asignar un char ** a un const char ** , pero le permitiría salirse con la suya asignando un char ** a un const char * const * .)