Почему константная функция-член может изменять статический член-данные?

86

В следующей C++программе изменение статического члена данных из constфункции работает нормально:

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

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

Но изменение нестатического члена данных из constфункции не работает:

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

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

Почему constфункция-член может изменять staticчлен данных?

msc
источник
Было бы полезно, если бы вы могли сообщить нам, с какой платформой и компилятором вы работаете? Таким образом, мы можем определить, является ли поведение ошибкой, связанной с вашей конкретной настройкой, или поведение действительно правильное, и его нужно просто объяснить.
Alex Zywicki
Компилятор @AlexZywicki G ++ на платформе Linux.
msc
8
Незачем. Это сделано намеренно, и все компиляторы C ++ должны его поддерживать. Но почему за такие хорошие вопросы больше не проголосуют?
Вирсавия
18
Это обман, но он лучше написан, чем другой, благодаря лучшему MCVE, поэтому я использовал его в качестве цели.
Baum mit Augen
5
Мотивация здесь в том, что constфункция-член объекта не может изменять этот единственный объект . Он может изменять другие объекты того же класса или staticданные, связанные с классом, а не его конкретный экземпляр. (Или mutableчлены данных, которые были созданы, чтобы быть исключением из этого правила.)
Дэвислор

Ответы:

100

Это правило, вот и все. И не зря.

constКлассификатор на функции члена означает , что вы не можете изменить Непро- mutableНепро- staticпеременных - членов класса.

Предлагая некоторую рационализацию, thisуказатель в constквалифицированной функции-члене является constтипом и thisпо своей сути связан с экземпляром класса. staticчлены не связаны с экземпляром класса. Вам не нужен экземпляр для изменения staticчлена: вы можете сделать это в вашем случае, написав A::a = 10;.

Итак, в первом случае считайте a = 10;это сокращением, A::a = 10;а во втором - сокращением для this->a = 10;, которое не компилируется, поскольку тип thisis const A*.

Вирсавия
источник
1
Здесь небольшая ошибка: поскольку вы не можете переназначить thisуказатель, он будет иметь тип const A* const в constслучае.
Тейлор Хансен,
2
@TaylorHansen this- это значение типа указателя. Prvalues ​​неклассовых типов никогда не квалифицируются cv.
21

Согласно стандарту C ++ (9.2.3.2 Статические элементы данных)

1 Статический член данных не является частью подобъектов класса ...

И (9.2.2.1 Указатель this)

1 В теле нестатической (9.2.1) функции-члена ключевое слово this является выражением prvalue, значение которого является адресом объекта, для которого вызывается функция. Тип this в функции-члене класса X - X *. Если функция-член объявлена ​​как const, ее типом будет const X * , ...

И наконец (9.2.2 Нестатические функции-члены)

3 ... если поиск по имени (3.4) разрешает имя в id-выражении в нестатический не типовой член некоторого класса C, и если либо id-выражение потенциально оценивается, либо C является X или базовым классом для X, выражение id преобразуется в выражение доступа к члену класса (5.2.5) с использованием (* this) (9.2.2.1) в качестве постфиксного выражения слева от. оператор.

Таким образом, в этом определении класса

class A 
{
  public:   
    static int a; 

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

статический член данных aне является подобъектом объекта типа класса, и указатель thisне используется для доступа к статическому члену данных. Таким образом, любая функция-член, нестатическая константа или неконстанта, или статическая функция-член может изменить член данных, потому что он не является константой.

В этом определении класса

class A 
{
  public:   
    int a; 

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

нестатический член данных aявляется подобъектом объекта типа класса. Для доступа к нему в функции-члене используется либо подразумевается синтаксис доступа к члену этого синтаксиса. Вы не можете использовать постоянный указатель thisдля изменения элемента данных. И указатель this действительно имеет тип const A *внутри функции, setпотому что функция объявлена ​​с квалификатором const. Если бы функция не имела квалификатора, в этом случае член данных можно было бы изменить.

Влад из Москвы
источник
13

Дело в том, что если функция-член класса Ais const, то тип thisis const X*и тем самым предотвращает изменение нестатических элементов данных (см., Например, стандарт C ++ ):

9.3.2 Указатель this [class.this]

В теле нестатической (9.3) функции-члена ключевое слово this представляет собой выражение prvalue, значение которого является адресом объекта, для которого вызывается функция. Тип this в функции-члене класса X - X *. Если функция-член объявлена ​​как const, ее типом будет const X *, ...

Если aэто нестатический член данных, то a=10это то же самое, что и this->a = 10, что недопустимо, если тип thisis const A*и aне был объявлен как mutable. Таким образом, поскольку void set() constделает тип thisсущества const A*, этот доступ не разрешен.

aНапротив, if - статический член данных, тогда a=10вообще не задействован this; и пока static int aсамо по себе не было объявлено как const, оператор a=10разрешен.

Стефан Лехнер
источник
1

constОтборочный на величинуХи функции члена означает , что вы не можете изменить non-mutable, non-static член класса .

Ли Куй
источник