c++ - que - Devolver un puntero de const a un miembro de datos const y la palabra clave ''auto''. Un poco confundido
punteros en c++ definicion (4)
Recientemente he estado aprendiendo C ++ y hoy mismo he sido introducido a const y al concepto de corrección de const. En un intento por comprender mejor la teoría, he estado escribiendo una serie de programas simples para asegurarme de que entiendo el concepto correctamente. Pensé que entendía todo, pero cuando uso la palabra clave auto en uno de los programas, parece que me he quedado un poco atascado.
Para probar que entendí cómo funcionan los punteros const, escribí un programa simple. No me molestaré en publicar todo esto, ya que solo hay dos partes relevantes. Tengo una clase con un miembro de datos const de tipo int:
const int tryToChangeMe;
Dentro de esta clase también tengo una función miembro que devuelve un puntero a la constante anterior:
const int* const MyClass::test()
{
return &tryToChangeMe;
}
En mi función principal, luego llamo a la función anterior, haciendo uso de la palabra clave auto . Para probar que lo que creo que sé sobre const es correcto, luego intento reasignar la variable tryToChangeMe a través del puntero. Al igual que:
auto temp = myClass.test();
*temp = 100;
Como esperaba, el programa no se compilaría debido al error que causé al intentar asignar un valor a una variable const . Sin embargo, no solo devolví un puntero a const , sino que devolví un puntero de const a una const (al menos eso es lo que pensé que hice). Entonces, para probar esto, intenté reasignar el puntero a una nueva dirección de memoria, bastante seguro de que obtendría un error de compilación similar:
temp = new int;
Pero bastante confuso el programa compilado sin ningún problema. Pasar por el depurador reveló que, efectivamente, el puntero estaba perdiendo su dirección original y se le asignó una nueva. Preguntándome qué estaba pasando, simplemente tuve la oportunidad de eliminar la palabra clave auto y reemplazarla con el tipo completo de la variable:
const int* const temp = myClass.test();
Al probar todo de nuevo, los resultados fueron los esperados y esta vez no pude reasignar el puntero a una nueva dirección.
Así que después de todo eso supongo que mi pregunta es, ¿por qué? ¿Por qué la palabra clave auto le permite omitir el calificador const de punteros? ¿Hice algo mal?
Por cierto, no estoy seguro de si es importante pero estoy usando la vista previa de Visual Studio 2015
Como ya se mencionó, auto
ignora los calificadores cv de nivel superior. Lea este artículo para conocer los detalles de cómo funcionan el auto
y el decltype
.
Ahora, incluso si auto
no ignoró la const
, en su caso, la temp
todavía no sería const
porque los calificadores cv de nivel superior en los tipos de devolución se ignored si el tipo que se devuelve no es de clase.
g ++ incluso produce la siguiente advertencia con -Wextra
advertencia: los calificadores de tipo ignorados en la función devuelven el tipo [-Wignored-qualifiers]
Esto se puede demostrar utilizando el decltype(auto)
C ++ 14 decltype(auto)
. A diferencia de auto
, decltype(auto)
no descarta las referencias y los calificadores cv de nivel superior. Si modifica su ejemplo agregando las siguientes líneas, el código aún se compilará, demostrando que la temp
no es un puntero const
.
decltype(auto) temp = myClass.test();
static_assert(std::is_same<const int*, decltype(temp)>{}, "");
Por otro lado, si test()
devuelve un objeto de tipo de clase con un calificador cv de nivel superior, entonces el auto
aún descartará const
, pero decltype(auto)
no.
Cuando escribes
auto temp = rhs;
La deducción de tipo funciona de la siguiente manera:
Si
rhs
es una referencia, entonces la referencia se ignora.los calificadores cv (constatable) de nivel superior de rhs también se ignoran (no se ignoran, sin embargo, si lo hace
auto& temp = rhs;
rhs;; en este caso, el patrón del compilador coincide con el tipo)
En su caso, el tipo del lado derecho es
const int* const
^^^^^
top-level cv qualifier
es decir, puntero de const
int
- int
. El puntero es como cualquier otra variable, por lo que se descartará su constancia (técnicamente, el calificador cv de nivel superior es const
y se descartará), por lo tanto, el tipo de temp
se deducirá como
const int*
es decir, un puntero no const
a const
, por lo que se puede volver a asignar. Si desea hacer cumplir la constancia, debe declarar el lado izquierdo como
const auto temp = myClass.test();
^^^^^
need this
Scott Meyers tiene una excelente introducción al tema (también disponible en su libro Effective Modern C ++ , Ítems 1 y 2 gratis para navegar here ), en el que explica cómo funciona la deducción de tipo de template
. Una vez que entiendas eso, entender el modo auto
es muy fácil, ya que auto
deducción de tipo de auto
realmente imita muy de cerca el sistema de deducción de tipo de plantilla (con la notable excepción de std::initializer_list<>
).
EDITAR
Hay una regla adicional para
auto&& temp = rhs;
pero para entenderlo, es necesario comprender cómo funcionan las referencias de reenvío (universales) y cómo funciona el colapso de referencias .
La razón es que auto
variables auto
no son const
por defecto. El hecho de que devuelva el valor const
no significa que deba asignarse a una variable const
; el valor se copia después de todo (aunque el valor es un puntero). Puedes intentarlo fácilmente con especificación de tipo explícita también. El valor almacenado en myClass
no se cambiará al cambiar la temp
variable y el objetivo del puntero sigue siendo const
por lo que aún se mantiene la constancia.
Proporcionaré una explicación formal del Estándar para ese hecho para los buscadores de referencia estándar:
La sección N4296::7.1.6.4/7 [dcl.spec.auto]
Si el marcador de posición es el especificador automático de tipo, el tipo deducido se determina utilizando las reglas para la deducción de argumentos de plantilla.
Ahora, el argumento de plantilla dedcution N4296::14.8.2/3 [temp.deduct]
:
Se realizan los ajustes de tipo de parámetro de función descritos en 8.3.5.
Y finalmente N4296::8.3.5/5 [dcl.fct]
Después de determinar el tipo de cada parámetro, cualquier parámetro del tipo "matriz de T" o "función que devuelve T" se ajusta para que sea "puntero a T" o "puntero a función que devuelve T", respectivamente. Después de generar la lista de tipos de parámetros, todos los calificadores cv de nivel superior que modifican un tipo de parámetro se eliminan al formar el tipo de función .
En resumen, sí, los calificadores de CV se ignoran en ese caso.