Search code examples
c++c++11referencereference-wrapper

C++ reference_wrapper vector fills with wrong values


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...


Solution

  • 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