variable - reference to class c++




Элемент данных ссылочного типа обеспечивает «лазейку» вокруг константности (2)

Недавно я наткнулся на следующую «лазейку» вокруг правильности const:

struct Inner {
  int field = 0;
  void Modify() {
    field++;
  }
};

struct Outer {
  Inner inner;
};

class MyClass {
public:
  Outer outer;
  Inner& inner; // refers to outer.inner, for convenience

  MyClass() : inner(outer.inner) {}

  void ConstMethod() const {
    inner.Modify();  // oops; compiles
  }
};

Кроме того, представляется возможным использовать эту лазейку для изменения объекта, объявленного как const , что, я считаю, является неопределенным поведением:

int main() {
    const MyClass myclass;
    std::cout << myclass.outer.inner.field << "\n";  // prints 0
    myclass.ConstMethod();
    std::cout << myclass.outer.inner.field << "\n";  // prints 1
}

Это пугает меня, потому что кажется, что я только что вызвал неопределенное поведение, связанное с const_cast в программе, которая не использует const_cast или не отбрасывает константность с использованием приведения в стиле C.

Итак, мои вопросы:

  • Правильно ли мне сказать, что вышеуказанная программа имеет неопределенное поведение?
  • Если так, это языковая ошибка? Есть ли в приведенной выше программе строка, которая, возможно, не должна (может быть разумно сделана не) компилироваться?
  • Есть ли какие-то руководящие принципы, которые следует соблюдать, чтобы избежать этой категории неопределенного поведения на практике?

Это не должно быть неопределенным поведением. Да, myclass.outer обрабатывается как const внутри MyClass::ConstMethod() const , но он не начал свое существование как const и, следовательно, его можно безопасно изменять с помощью const ссылок. Конечно, это может удивить читателей и / или пользователей вашего кода, поэтому вам следует избегать этого.

Это аналогично

int x = 5;
int * xPtr = &x;
const int * constXPtr = &x;

в котором существование constXPtr не constXPtr (и не должно мешать) изменять x через xPtr .

Стоит отметить, что такие возможности псевдонимов предотвращают много потенциальных оптимизаций компилятора.


Это не ошибка в коде, помните, что const относится к внутреннему элементу декларатора. В основном const на ConstMethod делает ссылку:

Inner& const inner;

Конечно, это не имеет никакого значения, потому что ссылки не могут быть связаны повторно. Вместо этого подумайте о том, чтобы сделать то же самое с указателем, и вы поймете, что inner все еще можно изменить. Если бы:

Inner * const inner;

Вы могли бы вызвать inner-> Modify ().





constants