简体   繁体   中英

C++11 member variable of reference type, different behaviour after vector push_back

I was using somebody else's class which was acting odd when I pushed it into a vector. It involves a member variable which is a reference to another member variable. Here is the smallest self-contained example:

#include <iostream>
#include <vector>

class Myclass {
public: 
  Myclass() : a(1.0) {}

  float a;
  float &a_ref = a;

  void addOne() {
    a = a + 1.0;
  }
};

int main() {
  Myclass instance1;
  instance1.addOne();

  //prints 2:
  std::cout << "instance.a_ref is " << instance1.a_ref << std::endl;

  std::vector<Myclass> vec;
  Myclass instance2;
  vec.push_back(instance2);

  vec.at(0).addOne();

  //prints 1;
  std::cout << "vec.at(0).a_ref is " << vec.at(0).a_ref << std::endl;
  return 0;
}

I was compiling with g++ and -std=c++11 , so I didn't notice the problem for a while. I see now the issue is probably to do with the synthesised copy constructor and the reference member. But what I'm not sure about is:

  1. Why is there different behaviour when the object is in a vector ?
  2. Why does g++ not give any warnings about this, using c++11 standard ?

Bonus question because I'm curious:

  1. What is initialized first, a or a_ref ?

The problem is indeed with the defaulted copy constructor. The defaulted copy constructor initialises all members from the members of the source object. That is, the defaulted copy constructor is identical to this:

Myclass(const Myclass &src) :
  a(src.a),
  a_ref(src.a_ref)
{}

The defaulted copy constructor initialises all members, so it ignores any in-class initialisers.

This is also why pushing into a vector causes the problem. vec.at(0) was created as a copy of instance2 , which means vec.at(0).a_ref refers to instance2.a . You could easily verify this by printing their addresses ( live example ).

The implicitly defined copy/move constructor:

[...] performs a performs a memberwise copy/move of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. [...]

In particular, reference members are direct-initialized to refer to the same object the corresponding reference member in the source object refers to.

So in your case, vec.at(0).a_ref refers to the member a of instance2 .

This is not detected by the compiler because in general reference members are expected to refer to a longer-lived object outside the class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM