Получение снова std :: map изменяет предыдущий итератор - PullRequest
1 голос
/ 27 мая 2020

Я не могу найти подобных вопросов. В тот момент, когда я вызываю getMap, предыдущий итератор, кажется, меняется:

//IF I COMMENT THE EVIL PRINT, THE PROBLEM DOES NOT OCCUR
std::cout << "EVIL PRINT" << std::endl;    
Something something;
auto mapElement = something.getTheMap().find("A");
std::cout << "Before: " << mapElement->first << std::endl;
something.getTheMap();
std::cout << "After: " << mapElement->first << std::endl << std::endl;

/****************************************************************************************/

//WITH SHARED POINTERS, THE EVIL PRINT IS NOT NECCESARY TO MAKE THE PROBLEM OCCUR
std::shared_ptr<Something> somePtr;
auto mapElement2 = something.getTheMap().find("A");
std::cout << "Before: " << mapElement2->first << std::endl;
something.getTheMap();
std::cout << "After: " << mapElement2->first << std::endl << std::endl;

OUTPUT:

EVIL PRINT
Before: A
After: B

Before: A
After: B

Здесь можно запустить полный код https://coliru.stacked-crooked.com/a/66b48636a476ddb7

Это желаемое поведение? Что происходит?

Ответы [ 2 ]

2 голосов
/ 27 мая 2020

Вы не включили самые важные части в свой вопрос, а именно:

std::map <std::string, int> getTheMap() {
        return theMap;
}

getTheMap возвращает копию, поэтому getTheMap().find("A"); возвращает итератор временному объекту (который перестает существовать после вызова завершается).
Следовательно, этот итератор ссылается на объект, который больше не существует, это висящий итератор. Разыменование его (как и в случае с mapElement->first) вызывает неопределенное поведение

Наиболее идиоматическое c исправление будет для getTheMap, чтобы вернуть ссылку, например:

std::map <std::string, int>& getTheMap() {
        return theMap;
}
1 голос
/ 27 мая 2020

У вас есть Undefined Behavior, потому что вы ссылаетесь на карту вне ее времени жизни.

getTheMap() возвращает карту по значению, что означает, что вы получаете копию исходной карты. Вы никогда не сохраняете эту копию где-либо, поэтому итератор сразу после создания становится болтающимся. снаружи):

std::map <std::string, int>& getTheMap() {
    return theMap;
}

Или сохраните карту копирования, чтобы убедиться, что она существует при использовании итератора

auto map = something.getTheMap();
auto mapElement = map.find("A");
...