¿Por qué una función de miembro constante puede modificar un miembro de datos estáticos?

86

En el siguiente C++programa, la modificación de un miembro de datos estáticos de una constfunción funciona bien:

class A 
{
  public:   
    static int a; // static data member

    void set() const
    {
        a = 10;
    }
};

Pero modificar un miembro de datos no estático de una constfunción no funciona:

class A 
{
  public:   
    int a; // non-static data member

    void set() const
    {
        a = 10;
    }
};

¿Por qué una constfunción de miembro puede modificar un staticmiembro de datos?

msc
fuente
Sería útil si nos pudiera decir con qué plataforma y compilador está trabajando. Entonces podemos determinar si el comportamiento es un error relacionado con su configuración específica o si el comportamiento es realmente correcto y solo necesita ser explicado.
Alex Zywicki
Compilador @AlexZywicki G ++ en plataforma Linux.
msc
8
No hay necesidad. Es intencional y todos los compiladores de C ++ deben admitirlo. Pero, ¿por qué ya no se vota a favor de buenas preguntas como esta?
Betsabé
18
Es un engaño, pero está mejor escrito que el otro gracias a un mejor MCVE, así que lo usé como objetivo.
Baum mit Augen
5
La motivación aquí es que constsignifica que una función miembro de un objeto no puede modificar ese objeto . Puede modificar otros objetos de la misma clase, o staticdatos, que están asociados con la clase, no una instancia particular de ella. (O mutablemiembros de datos, que fueron creados para ser la excepción a esta regla)
Davislor

Respuestas:

100

Es la regla, eso es todo. Y por una buena razón.

El constcalificador en una función miembro significa que no puede modificar variables miembro que mutableno sean de staticclase.

Para ofrecer alguna racionalización, el thispuntero en una constfunción miembro calificada es un consttipo y thisestá inherentemente relacionado con una instancia de una clase. staticlos miembros no están relacionados con una instancia de clase. No necesitas una instancia para modificar un staticmiembro: puedes hacerlo, en tu caso, escribiendo A::a = 10;.

Entonces, en su primer caso, piense a = 10;en una abreviatura de A::a = 10;y en el segundo caso, piense en ello como una abreviatura de this->a = 10;, que no es compilable ya que el tipo de thises const A*.

Betsabé
fuente
1
Solo un pequeño error aquí: dado que no puede reasignar el thispuntero, sería de tipo const A* const in const.
Taylor Hansen
2
@TaylorHansen thises un valor de tipo puntero. Los valores pr de tipos que no son de clase nunca están calificados por cv.
21

Según el estándar C ++ (9.2.3.2 miembros de datos estáticos)

1 Un miembro de datos estáticos no es parte de los subobjetos de una clase ...

Y (9.2.2.1 El puntero this)

1 En el cuerpo de una función miembro no estática (9.2.1), la palabra clave this es una expresión prvalue cuyo valor es la dirección del objeto para el que se llama a la función. El tipo de esto en una función miembro de una clase X es X *. Si la función miembro se declara const, el tipo de esto es const X * , ...

Y por fin (9.2.2 Funciones miembro no estáticas)

3 ... si la búsqueda de nombre (3.4) resuelve el nombre en la expresión id en un miembro no tipo no estático de alguna clase C, y si la expresión id se evalúa potencialmente o C es X o una clase base de X, la expresión-id se transforma en una expresión de acceso de miembro de clase (5.2.5) usando (* this) (9.2.2.1) como la expresión-sufijo a la izquierda de. operador.

Así, en esta definición de clase

class A 
{
  public:   
    static int a; 

    void set() const
    {
        a = 10;
    }
};

el miembro de datos estáticos ano es un subobjeto de un objeto del tipo de clase y el puntero thisno se utiliza para acceder al miembro de datos estáticos. Entonces, cualquier función miembro, constante no estática o no constante, o una función miembro estática puede cambiar el miembro de datos porque no es una constante.

En esta definición de clase

class A 
{
  public:   
    int a; 

    void set() const
    {
        a = 10;
    }
};

el miembro de datos no estáticos aes un subobjeto de un objeto del tipo de clase. Para acceder a él en una función de miembro, se utiliza una sintaxis de acceso de miembro de esta sintaxis. No puede utilizar un puntero constante thispara modificar el miembro de datos. Y el puntero que es de hecho tiene tipo const A *dentro de la función setporque la función se declara con el calificador const. Si la función no tiene el calificador en este caso, el miembro de datos podría cambiarse.

Vlad de Moscú
fuente
13

El caso es que si una función miembro de una clase Aes const, entonces el tipo de thises const X*y, por lo tanto, evita que se modifiquen los miembros de datos no estáticos (cf, por ejemplo, el estándar C ++ ):

9.3.2 El puntero this [class.this]

En el cuerpo de una función miembro no estática (9.3), la palabra clave this es una expresión prvalue cuyo valor es la dirección del objeto para el que se llama a la función. El tipo de esto en una función miembro de una clase X es X *. Si la función miembro se declara const, el tipo de esto es const X *, ...

Si aes un miembro de datos no estático, entonces a=10es el mismo que this->a = 10, que no está permitido si el tipo de thises const A*y ano se ha declarado como mutable. Así, dado que void set() consthace el tipo de thisser const A*, este acceso no está permitido.

Si aes un miembro de datos estáticos, por el contrario, a=10no implica thisen absoluto; y siempre que static int ano se haya declarado por sí mismo como const, a=10se permite la declaración .

Stephan Lechner
fuente
1

El constcalificador en una función miembro de medios que no se puede modificar non-mutable, non-static miembros de datos de clase .

Li Kui
fuente