C ++ reference_wrapper vector заполняется неправильными значениями - PullRequest
1 голос
/ 19 марта 2020

Я пытаюсь использовать ссылочные оболочки в C ++, так как я хочу иметь возможность напрямую изменять значения объектов, но все их значения неверны. Вот пример, который демонстрирует эту проблему:

#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 << " ";
    }
}

Он печатает

1 2 3
3 3 3

Я не уверен, почему опорный вектор-обертка v2 не копирует указанные значения в v1 ...

РЕДАКТИРОВАТЬ: Вот еще один пример, который также таинственно не работает. Я не думаю, что в этом есть какие-либо свисающие ссылки.

#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 << " ";
    }
}

Он печатает 9596312 1 2, в то время как я ожидаю, что он напечатает 0 1 2 ...

Ответы [ 2 ]

3 голосов
/ 19 марта 2020

В for l oop

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

Вы создаете reference_wrapper s из локальной переменной i, которая уничтожается сразу после итерации, поэтому reference_wrapper s хранящиеся в v2 болтаются. Обращение к ним приводит к неопределенному поведению .

Вы должны объявить i как ссылку как

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

LIVE


Для вашего редактирования обратите внимание, что std::vector::push_back может вызвать перераспределение на v1, что делает ссылки, хранящиеся в v2, ссылающиеся на элементы v1, повисшие.

Вы можете использовать reserve заранее, чтобы избежать перераспределения. например,

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

1 голос
/ 19 марта 2020

Это:

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

создает reference_wrapper<int> на push_back. Это будет ссылка на i в l oop. Так случилось, что ваша реализация компилятора, скорее всего, использовала один и тот же адрес памяти для переменной scoped i.

Ссылки будут по-прежнему ссылаться на этот самый адрес после l oop - который остается нетронутым в конце l oop, так что последнее значение, которое имело i, хранится там.

Это приводит к тому, что программа имеет неопределенное поведение для чтения / записи по этому адресу через ссылки (или любые другие средства) и одно неопределенное поведение состоит в том, что вы действительно можете прочитать память и извлечь из нее значения, которые кажутся знакомыми.

Переменная i не работает после l oop, поэтому вам следует обращаться со ссылками к этому так же. Они все болтаются на ветру ...

...