0

Suppose that I have a class with a mutable member, and I take multiple const references to an object of that class. Does the standard guarantee that these references will stay in sync with one another? An example is below.

Motivation: I believe that I have read that the compiler has multiple options on how to extend the lifetime of objects with const references, and some of them actually involve copying the object (which of course may subvert one of the reasons for using references in the first place, but may be necessary to guarantee the object's survival). If this is true, then can the multiple references actually start to act like multiple objects (with different values for the mutable members)?

Example:

class A {
  public:
    mutable int x;
    A( const int x ) : x( x ) {}
    void f() const;  // Actually changes 'x'.
};

class B {
  public:
    const A & a;
    B( const A & a ) : a( a ) {}
    void f() const { a.f(); }
};

int main() {
  B * b1;
  B * b2;
  {
    A a( 0 );
    b1 = new B( a );
    b2 = new B( a );
  }

  // Do something that would change a mutable variable in 'b1.a' but possibly not in 'b2.a'.
  b1.f();

  return 0;
}

Here is how I am interpreting this. a has already gone out of scope, so the compiler had to either "keep the original object alive" somehow (which doesn't mix well with my basic understanding of how the stack works) or make a copy of it for each instance of B (which the compiler assumes to be safe because it is const). However, I did something that may have changed the mutable part of one of these instances. Can b1.a and b2.a actually differ at this point?

If the answer is "yes," then this is reasonable, since I wouldn't expect otherwise. But if the answer is "no," I am very interested in how it works.

sasquires
  • 356
  • 3
  • 15

1 Answers1

2

It seems you were expecting lifetime extension due to reference binding. Nope, sorry. Lifetime extension is for temporaries, not objects that already exist with a scope and a name.

Even if you'd passed a temporary to B's constructor, it'd still only be extended until the end of that statement — the extension isn't transitive along a whole chain of reference bindings (and it doesn't work on member references).

Therefore, you're trying to access data through a dangling reference, and your program has undefined behaviour. Expect any result, or no result. Neither const nor mutable has anything to do with it.

If you're wondering where physically your observations originate, that's something to take up with your preferred deity.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • And no there is no lifetime extension here. You can't just plonk a reference at a random place in your program and extend the lifetime of an arbitrary variable! – Lightness Races in Orbit Feb 28 '18 at 01:23
  • The "lifetime extension" I am referring to is the ordinary one for const references. See for example [GotW 88](https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/): "However, C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error." (continued in next comment) – sasquires Feb 28 '18 at 02:14
  • But the same paragraph gives what may be the real answer to my question: "(Note this only applies to stack-based references. It doesn’t work for references that are members of objects.)" – sasquires Feb 28 '18 at 02:14
  • 1
    @sasquires: Lifetime extension is for temporaries, not for objects that already exist and have a scope and a name. The GotW quote does specific _temporary_ object. – Lightness Races in Orbit Feb 28 '18 at 02:16
  • Ah, somehow I skipped the word "temporary" in that sentence too. Ok, this makes sense, and my question doesn't. – sasquires Feb 28 '18 at 02:17
  • @sasquires: Agreed :P – Lightness Races in Orbit Feb 28 '18 at 02:17