3

Let's have a simple class:

class Var
{
public:
    explicit Var(const std::string& name, const double v) 
        : value(v), ref(value), _name(name) {};
    ~Var() {};

    double value{};
    double& ref;

    void print()
    {
        std::cout << _name << ":\n";
        std::cout << "\tvalue = " << value << " (" << &value << ")\n";
        std::cout << "\t  ref = " << ref << " (" << &ref << ")\n\n";
    }

private:
    std::string _name;
};

In this case everything is fine:

Var v0("v0", 0.0);
Var v1("v1", 1.0);
Var v2("v2", 2.0);

v0.print();
v1.print();
v2.print();

Output is:

v0:
 value = 0 (000000DDE3D3F878) 
 ref = 0 (000000DDE3D3F878) 
v1:
 value = 1 (000000DDE3D3F8E8) 
 ref = 1 (000000DDE3D3F8E8) 
v2: 
 value = 2 (000000DDE3D3F958) 
 ref = 2 (000000DDE3D3F958)

But when objects are placed in a vector, the ref variable is the same for all objects.

vector<Var> V{};
for (size_t i = 0; i < 3; i++)
{
    std::string name = "V[" + std::to_string(i) + "]";
    V.push_back(Var(name, i));
}

for (size_t i = 0; i < 3; i++)
    V[i].print();

output:

V[0]:
    value = 0 (000002B594F55C70)                                                      
      ref = 2 (000000DDE3D3FA88)
V[1]:
    value = 1 (000002B594F55CA8)                                                      
      ref = 2 (000000DDE3D3FA88)
V[2]:
    value = 2 (000002B594F55CE0)                                                      
      ref = 2 (000000DDE3D3FA88) 

what am I doing wrong?

Blockquote

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • vector does a lot of copying, assigning, and moving and referecences... can't be reassigned to refer to something else. Its a pain in the to have a reference as a non-`static` class member. – user4581301 Oct 06 '20 at 20:56
  • To see where this is headed, add in `std::vector V2{}; V2 = V;` and watch the compiler errors fly. – user4581301 Oct 06 '20 at 21:05
  • ok, adding a copy constructor (or removing it explicitly) or replacing a reference to a pointer is a way out of this situation. I'm still wondering why the addresses of ref are the same? – hermann.minkowski Oct 06 '20 at 22:01
  • Use a `std::reference_wrapper`. Untangling the reference in the copy constructor is just too damn much work. – user4581301 Oct 06 '20 at 22:19

1 Answers1

4

std::vector requires an appropriately written assignment operator.

The one the compiler provides is useless for your class since the reference is not rebound. So you need to write it out yourself. And that's not trivial:

Assignment operator with reference members

The best thing to do though is to drop the reference class member.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Yes, this is an artificial example. I was interested in this behavior. emplace_back() will save the situation? – hermann.minkowski Oct 06 '20 at 21:10
  • Using std::reference_wrapper would be better. – Bathsheba Oct 06 '20 at 21:35
  • I'm still wondering why the addresses of ref are the same? – hermann.minkowski Oct 06 '20 at 21:46
  • 2
    It's not a coincidence that the addresses of the `ref` are the same. The reason is that you're creating an object of the same type (= same size) on the stack, on each iteration. Therefore, `ref` (which under the hood is a pointer) points to the same area, which have the same address and the contents of the last object created there. Since you're not re-binding `ref`, as pointed out in the answer, that's one of the possible outcomes. **Note** that formally doing that is undefined behavior in C++. – vvaltchev Oct 06 '20 at 22:11
  • 1
    @hermann.minkowski `emplace_back` can't completely save you. as the `vector` gets resized, the default copy constructor stupidly does something along the lines of `Var(const Var& src) : value(src.value), ref(src.ref), _name(src._name) { }` and `src.ref` is referring to the `value` member of `Var` that's about to die or a `Var` that's already long dead.. – user4581301 Oct 06 '20 at 22:21
  • 1
    @vvaltchev thanks! This is what I was looking for. Probably, it was necessary to formulate the question in this way initially. – hermann.minkowski Oct 06 '20 at 22:22