Por que uma função de membro const pode modificar um membro de dados estáticos?

86

No C++programa a seguir , a modificação de um membro de dados estáticos de uma constfunção funciona bem:

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

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

Mas modificar um membro de dados não estático de uma constfunção não funciona:

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

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

Por que uma constfunção de membro pode modificar um staticmembro de dados?

msc
fonte
Seria útil se você pudesse nos dizer com qual plataforma e compilador está trabalhando? Assim, podemos determinar se o comportamento é um bug relacionado à sua configuração específica ou se o comportamento está realmente correto e só precisa ser explicado.
Alex Zywicki
Compilador @AlexZywicki G ++ na plataforma Linux.
msc
8
Não há necessidade. É intencional e todos os compiladores C ++ devem suportá-lo. Mas por que boas perguntas como essa não têm mais votos positivos?
Bathsheba
18
É um dupe, mas está melhor escrito do que o outro graças a um MCVE melhor, então usei isso como alvo.
Baum mit Augen
5
A motivação aqui é que constsignifica que uma função membro de um objeto não pode modificar aquele objeto . Ele pode modificar outros objetos da mesma classe, ou staticdados, que estão associados à classe, não qualquer instância particular dela. (Ou mutablemembros de dados, que foram criados para ser a exceção a esta regra.)
Davislor

Respostas:

100

É a regra, só isso. E por um bom motivo.

O constqualificador em uma função de membro significa que você não pode modificar variáveis ​​de membros que mutablenão sejam de staticclasse.

Para oferecer alguma racionalização, o thisponteiro em uma constfunção de membro qualificado é um consttipo e thisestá inerentemente relacionado a uma instância de uma classe. staticmembros não estão relacionados a uma instância de classe. Você não precisa de uma instância para modificar um staticmembro: você pode fazer isso, no seu caso, por escrito A::a = 10;.

Portanto, em seu primeiro caso, pense a = 10;em uma abreviatura para A::a = 10;e, no segundo caso, pense nisso como uma abreviação de this->a = 10;, que não é compilável porque o tipo de thisé const A*.

Bate-Seba
fonte
1
Apenas um pequeno erro aqui: Já que você não pode reatribuir o thisponteiro, seria do tipo const A* const no constcaso de.
Taylor Hansen
2
@TaylorHansen thisé um prvalue do tipo de ponteiro. Prvalues ​​de tipos não-classe nunca são qualificados para cv.
21

De acordo com o padrão C ++ (9.2.3.2 Membros de dados estáticos)

1 Um membro de dados estáticos não faz parte dos subobjetos de uma classe ...

E (9.2.2.1 O indicador this)

1 No corpo de uma função de membro não estática (9.2.1), a palavra-chave this é uma expressão prvalue cujo valor é o endereço do objeto para o qual a função é chamada. O tipo disso em uma função-membro de uma classe X é X *. Se a função de membro for declarada const, o tipo disso é const X * , ...

E, por último (9.2.2 Funções de membro não estáticas)

3 ... se a pesquisa de nome (3.4) resolve o nome na expressão id para um membro não estático de alguma classe C, e se a expressão id é potencialmente avaliada ou C é X ou uma classe base de X, a expressão id é transformada em uma expressão de acesso de membro de classe (5.2.5) usando (* this) (9.2.2.1) como a expressão pós-fixada à esquerda do. operador.

Portanto, nesta definição de classe

class A 
{
  public:   
    static int a; 

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

o membro de dados estáticos anão é um subobjeto de um objeto do tipo de classe e o ponteiro thisnão é usado para acessar o membro de dados estáticos. Portanto, qualquer função de membro, constante não estática ou não constante, ou uma função de membro estática pode alterar o membro de dados porque ele não é uma constante.

Nesta definição de classe

class A 
{
  public:   
    int a; 

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

o membro de dados não estáticos aé um subobjeto de um objeto do tipo de classe. Para acessá-lo em uma função de membro, é usada uma sintaxe de acesso de membro desta sintaxe está implícita. Você não pode usar um ponteiro constante thispara modificar o membro de dados. E o ponteiro this realmente tem tipo const A *dentro da função setporque a função é declarada com o qualificador const. Se a função não tivesse o qualificador, neste caso, o membro de dados poderia ser alterado.

Vlad de Moscou
fonte
13

O fato é que, se uma função de membro de uma classe Afor const, o tipo de thisé const X*e, portanto, evita que membros de dados não estáticos sejam alterados (cf, por exemplo, padrão C ++ ):

9.3.2 O ponteiro this [class.this]

No corpo de uma função de membro não estática (9.3), a palavra-chave this é uma expressão prvalue cujo valor é o endereço do objeto para o qual a função é chamada. O tipo disso em uma função-membro de uma classe X é X *. Se a função de membro for declarada const, o tipo disso é const X *, ...

Se afor um membro de dados não estático, então a=10é o mesmo que this->a = 10, o que não é permitido se o tipo de thisé const A*e anão foi declarado como mutable. Assim, uma vez que void set() constfaz o tipo de thisser const A*, esse acesso não é permitido.

Se afor um membro de dados estático, em contraste, então a=10não envolve thisnada; e enquanto static int apor si só não foi declarado como const, declaração a=10é permitida.

Stephan Lechner
fonte
1

O constqualificador em uma função de membro significa que você não pode modificar non-mutable, non-static membros de dados de classe .

Li Kui
fonte