Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference to member variable broken after adding another instance to the same vector

Consider the following code:

struct Point {
    int x;
    int& xref = x;
};

int main() {
    std::vector<Point> points;
    for (int i = 0; i < 2; i++) {
        Point p;
        p.x = i;
        points.push_back(p);
        assert (points[0].x == points[0].xref);
    }

    return 0;
}

The assertion fails on the second iteration. Why?

I'm using the GNU C++ compiler. The problem occurs with all of the standards I've tested:

  • c++11
  • c++14
  • c++17
  • gnu++11
  • gnu++14
  • gnu++17

Every element's xref seems to reference the last added element's x instead of its own, as can be seen here: https://pastebin.com/H4wCszxp

like image 212
user2623008 Avatar asked Aug 27 '26 08:08

user2623008


2 Answers

When you push an object into a vector, you make a copy †.

When you copy a reference (that is the member), the new reference will refer to the same object as the one it was copied from. So, if you copy a Point p into a Point copy, then copy::xref will refer to p.x, because that's what p.xref refers to.

So, all Point objects that are within the vector are copies of the Point p automatic variables that were constructed within the scope of the loop. And therefore all of those objects in the vector refer to the int objects that are the member of the automatic variable p. None of them refer to their own x member.

During first iteration this is fine, because points[0].xref refers to an existing p.x, which also has the same value as points[0].x. But at the end of that iteration, the automatic variable p (whose member the points[0].xref refers to) is destroyed. At this point points[0].xref is a dangling reference that no longer refers to a valid object. In the next iteration the reference is used. Using a dangling reference has undefined behaviour.


If you wish to access this object, then use this pointer. If you want to store a reference to an object, then don't expect a copy of that reference to refer to another object. Avoid storing a reference (or a pointer) to an object that has shorter lifetime than the object that holds the reference.


† ... or make a move when you push an rvalue. You don't push an rvalue, and a copy is exactly the same thing as move for Point), so this is an irrelevant detail.

like image 125
eerorika Avatar answered Aug 29 '26 21:08

eerorika


This is happening because when the point is inserted into the vector it get copied, and the reference points to the original value. Eg. this work fine:

int main() {
    Point p;
    p.x = 100;
    assert (p.x == p.xref);

  return 0;
}
like image 36
Qitelia Avatar answered Aug 29 '26 21:08

Qitelia