Dlaczego funkcja składowa stałej może modyfikować statyczny element członkowski danych?

86

W poniższym C++programie modyfikowanie statycznego elementu członkowskiego danych z constfunkcji działa poprawnie:

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

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

Ale modyfikowanie niestatycznego elementu członkowskiego danych z constfunkcji nie działa:

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

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

Dlaczego constfunkcja członkowska może modyfikować składową staticdanych?

msc
źródło
Byłoby pomocne, gdybyś mógł nam powiedzieć, z jaką platformą i kompilatorem pracujesz? Dzięki temu możemy określić, czy zachowanie jest błędem związanym z konkretną konfiguracją, czy też jest rzeczywiście poprawne i wymaga tylko wyjaśnienia.
Alex Żywicki
Kompilator @AlexZywicki G ++ na platformę Linux.
msc
8
Nie ma potrzeby. Jest to zamierzone i wszystkie kompilatory C ++ muszą to obsługiwać. Ale dlaczego dobre pytania, takie jak to, nie są już popierane?
Batszeba
18
To podstęp, ale jest lepiej napisany niż drugi dzięki lepszemu MCVE, więc użyłem tego jako celu.
Baum mit Augen
5
Motywacja jest taka, constże funkcja składowa obiektu nie może modyfikować tego jednego obiektu . Może modyfikować inne obiekty tej samej klasy lub staticdane, które są skojarzone z klasą, a nie jej konkretne wystąpienie. (Lub mutableczłonkowie danych, którzy zostali stworzeni jako wyjątek od tej reguły.)
Davislor

Odpowiedzi:

100

To zasada, to wszystko. I nie bez powodu.

constKwalifikator na funkcję członek oznacza, że nie można modyfikować innych niż mutableinnych niż staticzmienne składowe klasy.

Aby thiszapewnić pewną racjonalizację, wskaźnik w constkwalifikowanej funkcji składowej jest consttypem i thisjest nieodłącznie powiązany z wystąpieniem klasy. staticelementy członkowskie nie są powiązane z instancją klasy. Nie potrzebujesz instancji, aby zmodyfikować staticczłonka: możesz to zrobić w swoim przypadku, pisząc A::a = 10;.

Tak więc, w pierwszym przypadku, myśleć a = 10;jako skrót do A::a = 10;i w drugim przypadku, myśleć o nim jako skrót dla this->a = 10;, których nie jest compilable od rodzaju thisjest const A*.

Batszeba
źródło
1
Wystarczy niewielki błąd tutaj: Ponieważ nie można przypisać ten thiswskaźnik, to byłoby typu const A* const w const„s sprawy.
Taylor Hansen
2
@TaylorHansen thisjest wartością typu wskaźnika. Wartości wartości nieklasowych nigdy nie są kwalifikowane jako CV.
21

Zgodnie ze standardem C ++ (9.2.3.2 Statyczne elementy składowe danych)

1 Statyczny element członkowski danych nie jest częścią podobiektów klasy ...

I (9.2.2.1 Ten wskaźnik)

1 W treści niestatycznej funkcji składowej (9.2.1) słowo kluczowe this jest wyrażeniem prvalue, którego wartością jest adres obiektu, dla którego funkcja jest wywoływana. Typ this w funkcji składowej klasy X to X *. Jeśli funkcja składowa jest zadeklarowana jako const, typ tego to const X * , ...

I wreszcie (9.2.2 Niestatyczne funkcje składowe)

3 ... jeśli wyszukiwanie nazw (3.4) rozwiązuje nazwę w wyrażeniu id na niestatyczną składową niebędącą typem jakiejś klasy C, i jeśli albo wyrażenie id jest potencjalnie oceniane, albo C jest X lub klasą bazową X, wyrażenie id jest przekształcane w wyrażenie dostępu do składowej klasy (5.2.5) przy użyciu (* this) (9.2.2.1) jako wyrażenia postfiksowego po lewej stronie. operator.

Tak więc w tej definicji klasy

class A 
{
  public:   
    static int a; 

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

statyczny element członkowski danych anie jest podobiektem obiektu typu klasy, a wskaźnik thisnie jest używany do uzyskania dostępu do statycznego elementu członkowskiego. Tak więc każda funkcja składowa, niestatyczna stała lub zmienna lub statyczna funkcja członkowska może zmienić element członkowski danych, ponieważ nie jest stałą.

W tej definicji klasy

class A 
{
  public:   
    int a; 

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

niestatyczny element członkowski danych ajest podobiektem obiektu typu klasy. Aby uzyskać do niego dostęp w funkcji składowej, używana jest składnia dostępu do elementów składowych o tej składni. Nie można używać stałego wskaźnika thisdo modyfikowania elementu członkowskiego danych. Wskaźnik ten rzeczywiście ma typ const A *wewnątrz funkcji, setponieważ funkcja jest zadeklarowana z kwalifikatorem const. Jeśli funkcja nie ma kwalifikatora, w tym przypadku element członkowski danych można zmienić.

Vlad z Moskwy
źródło
13

Chodzi o to, że jeśli funkcja składowa klasy Ajest const, to typ thisjest const X*, a tym samym zapobiega zmianie niestatycznych składowych danych (por. Na przykład standard C ++ ):

9.3.2 Wskaźnik this [class.this]

W treści niestatycznej (9.3) funkcji składowej słowo kluczowe this jest wyrażeniem prvalue, którego wartością jest adres obiektu, dla którego funkcja jest wywoływana. Typ this w funkcji składowej klasy X to X *. Jeśli funkcja składowa jest zadeklarowana jako const, typ tego to const X *, ...

Jeśli ajest niestatycznym a=10składnikiem danych, to jest taki sam jak this->a = 10, co nie jest dozwolone, jeśli typ thisjest const A*i anie został zadeklarowany jako mutable. Tak więc, ponieważ void set() consttworzy typ thisbytu const A*, ten dostęp nie jest dozwolony.

aNatomiast jeśli jest statycznym składnikiem danych, to w a=10ogóle nie obejmuje this; i dopóki samo static int aw sobie nie zostało zadeklarowane jako const, instrukcja a=10jest dozwolona.

Stephan Lechner
źródło
1

constKwalifikator on a funkcja składowa oznacza, że nie można modyfikować non-mutable, non-static członków danych klasy .

Li Kui
źródło