1

I'm trying to use reference wrappers in C++, since I want to be able to directly alter objects' values, but their values are all wrong. Here's an example that demonstrates this problem:

#include <vector>
#include <functional>
#include <iostream>
using namespace std;

int main() {
    vector<int> v1 = {1, 2, 3};
    for (int i : v1) {
        cout << i << " ";
    }
    cout << endl;

    vector<reference_wrapper<int>> v2;
    for (int i : v1) {
        v2.push_back(i);
    }
    for (int i : v2) {
        cout << i << " ";
    }
}

It prints

1 2 3
3 3 3

I'm not sure why the reference wrapper vector v2 is not copying the referred values in v1...

EDIT: Here's another example that also mysteriously doesn't work. I don't think there are any dangling references in this one.

#include <vector>
#include <functional>
#include <iostream>
using namespace std;

int main() {
    vector<int> v1;
    vector<reference_wrapper<int>> v2;

    for (int i = 0; i < 3; i++) {
        v1.push_back(i);
        v2.push_back(v1[i]);
    }

    for (int i : v2) {
        cout << i << " ";
    }
}

It prints 9596312 1 2 while I expect it to print 0 1 2...

Jason
  • 302
  • 2
  • 10
  • Nasty ... you are inadvertently emplacing a ref to `i`, not the value in `v1` you want. I don't know how to formulate that good enough in words, but you'll end up with a `v2` full of dangling references if I read the code right. `3 3 3` is a very possible outcome of undefined behavior. – Ted Lyngmo Mar 19 '20 at 05:35
  • Your new example is caused by a different issue (reallocation). Please don't ask several questions in one question like this. – L. F. Mar 19 '20 at 08:07

2 Answers2

3

In the for loop

for (int i : v1) {
    v2.push_back(i);
}

You're constructing reference_wrappers from the local variable i, which is destroyed immediately after the iteration, so reference_wrappers stored in v2 are dangled. Dereference on them leads to undefined behavior.

You should declare i as reference as

for (int& i : v1) {
    v2.push_back(i);
}

LIVE


For your edit, note that std::vector::push_back might cause reallocation on v1, which makes the references stored in v2 refering to the elements of v1 dangled.

You can use reserve in advance to avoid reallocation. e.g.

vector<int> v1;
v1.reserve(3);
vector<reference_wrapper<int>> v2;

for (int i = 0; i < 3; i++) {
    v1.push_back(i);
    v2.push_back(v1[i]);
}

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

This:

    vector<reference_wrapper<int>> v2;
    for (int i : v1) {
        v2.push_back(i);
    }

constructs a reference_wrapper<int> per push_back. It'll be a reference to the i in the loop. As it happens, your compiler implementation most probably happened to reuse the same memory address for the scoped variable i.

The references will still reference that very address after the loop - which happens to be untouched at the end of the loop so the last value i had is what is stored there.

It causes the program to have undefined behavior to read/write at that address via the references (or by any other means) and one undefined behavior is that you actually can read the memory and get values out of it that seem familiar.

The variable i is dead after the loop so you should treat references to it the same way. They are all dangling in the wind ...

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108