Почему ссылки могут быть изменены с помощью функций-членов?

Ниже будет компилировать, хотя функция const member будет изменять значение члена. Как это так?

#include <iostream>

struct foo
{
    std::string &str;
    foo(std::string &other) : str(other) {}

    void operator()(std::string &some) const
    {
        str += some;
    }
};

int main()
{
    std::string ext("Hello");
    foo a{ ext };

    std::string more(" world!");
    a(more);

    cout << a.str;
    return 0;
}

Ответ 1

Квалификатор const функции члена класса указывает, что эта функция-член (например, foo::operator() const) не может изменять состояние объекта с точки зрения клиента (т.е. это абстрактное состояние). Это не совсем то же самое, что сказать, что исходные биты объекта не будут меняться.

Запрещается компиляторам С++ рассматривать объекты как необработанные биты, если они не могут решить проблему проблему сглаживания. что в вашем случае компилятор не может. Это связано с тем, что существует непостоянный псевдоним (т.е. std::string &str), и, следовательно, состояние объекта может быть изменено.

То есть вызов operator() объекта a не изменяет состояние a (т.е. хотя ext изменился, str по-прежнему остается псевдонимом ext).

Это также объясняет, почему указание на объект с указателем на константу (т.е. std::string * const str) не гарантирует, что объект не будет изменен. Это гарантирует только то, что объект не изменится с помощью этого указателя.

Ответ 2

Рассмотрим ссылку как классический указатель:

#include <iostream>

struct foo
{
    std::string * const str; // is the same as std::string &str
    foo(std::string &other) : str(&other) {}

    void operator()(std::string &some) const
    {
        *str += some;
    }
};

int main()
{
    std::string ext("Hello");
    foo a{ ext };

    std::string more(" world!");
    a(more);

    cout << a.str;
    return 0;
}

Вы увидите, что указатель не изменяется, но только указанное в нем значение.