Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data member of reference type provides "loophole" around const-correctness

I've recently stumbled onto the following "loophole" around const-correctness:

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
  }
};

It further appears to be possible to use this loophole to modify an object declared as const, which I believe is undefined behaviour:

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
}

This scares me, because it seems like I've just invoked undefined behaviour related to const-correctness in a program that doesn't use const_cast or cast away constness using a C-style cast.

So, my questions are:

  • Am I correct to say that the above program has undefined behaviour?
  • If so, is this a language bug? Is there a line in the above program that arguably shouldn't (could reasonably made not to) compile?
  • Are there some guidelines that should be followed to avoid this category of undefined behaviour in practice?
like image 494
HighCommander4 Avatar asked Jul 20 '18 18:07

HighCommander4


2 Answers

Any modifications to a const object is undefined behaviour, and the snippet does indeed do that.

The program isn't ill-formed (which would then require a compile error), since at the point of initializing inner, cv-qualifiers haven't taken effect yet.

From the compiler's standpoint, to issue a warning would require it to analyze all code paths leading up to inner.Modify() and proving that inner must be referring to a const object, which is impossible in the general case.

The best suggestion is likely to not have internal pointers/references, which are evil anyways.

like image 116
Passer By Avatar answered Nov 16 '22 10:11

Passer By


That is not an error in code, remember that const refers to the innermost element of the declarator. Basically the const on ConstMethod makes the reference:

Inner& const inner;

Of course that does not make any real difference because references cannot be re-bound. Think of doing the same thing with a pointer instead and you would realize that inner could still be modified. If it were:

Inner * const inner;

you could call inner->Modify().

like image 1
SoronelHaetir Avatar answered Nov 16 '22 09:11

SoronelHaetir