C ++ возвращает ссылку на значение в векторе - PullRequest
0 голосов
/ 24 октября 2018

Для следующей программы:

#include <iostream>
#include <utility>
#include <vector>
#include <unordered_map>

#ifdef WITHPAIR
auto get_state() {
    std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> st;

    auto& v = st.first;
    auto& index = st.second;

    v.assign({1u,2u,3u,4u});

    index.insert({0u, v[0]});
    index.insert({1u, v[1]});
    index.insert({2u, v[2]});
    index.insert({3u, v[3]});

    return st;
}

#else

std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> get_state() {
    std::vector v{1u,2u,3u,4u};
    std::unordered_map<unsigned, unsigned&> index{
            {0u, v[0]},
            {1u, v[1]},
            {2u, v[2]},
            {3u, v[3]}
    };

    return {v, index};
}
#endif

auto main() -> int {
    auto [v, index] = get_state();
//    auto [v, index] = std::move(get_state());
    std::cout << v[0] << " " << index.at(0) << std::endl;
    v[0] = 5;
    std::cout << v[0] << " " << index.at(0) << std::endl;
    std::cout << v[1] << " " << index.at(1) << std::endl;
    v[2] = 17;
    std::cout << v[2] << " " << index.at(2) << std::endl;
    std::cout << v[3] << " " << index.at(3) << std::endl; 

    return 0;
}

http://coliru.stacked -crooked.com / a / f9e528074ae78c03

Компиляция без -DWITHPAIR для просмотра поведения секундыfunction


Существует две версии функции get_state.

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

У меня два вопроса:

  • Правильно ли поведение первой функции?Это неопределенное поведение или нет?
  • Почему второй меняет вектор?

1 Ответ

0 голосов
/ 24 октября 2018

Ваша первая функция работает из-за NRVO :

В операторе возврата, когда операндом является имя энергонезависимого объекта с длительностью автоматического хранения,который не является параметром функции или параметром предложения catch и имеет тот же тип класса (игнорирующий квалификацию cv), что и тип возвращаемого функцией.Этот вариант удаления копии известен как NRVO, «именованная оптимизация возвращаемого значения».

, если это происходит, копирование не выполняется, а ссылки на карте все еще действительны, но поскольку NRVO является оптимизацией ине гарантируется, что вам просто повезло, что это работает.

Во второй функции вы создаете временный объект типа std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> и используете для его инициализации lvalues ​​v и index, поэтому конструкторы копирования вызываются для обоихиз них и все ссылки становятся недействительными, когда оригинал v уничтожен.Исправить можно было бы вызвать std::move явно:

return {std::move(v), index};

и в качестве стандарта гарантирует, что ссылки останутся действительными и будут указывать на элементы в новом векторе, поэтому не должно быть больше UB.index также следует перенести для эффективности и согласованности, но я оставил это, поскольку это не связано с вопросом.

Это довольно опасный способ программирования, и я бы рассмотрел разные типы данных (вероятно, индекс в векторе вместо ссылки).

...