Сегодня я наткнулся на этот простой код.
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
int main(int argc, char* argv[]) {
std::unordered_map<std::string, int> m = {
{"asdf", 123},
{"dsdfesjfdaslkfjasljlasfjlasjfakdlsfjasklfajsklfjaskljlf", 123},
{"ldfjaslk}sfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
{"ldafjaslk}sfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
{"ldjaksdfjfjaslk}sfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
{"ldfjaslk}sfdasfdsfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
{"ldfjaslk}sfjalsksfdasdfdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
{"ldfjaslk}sfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj", 1},
};
std::vector<std::pair<std::string_view, int>> vec;
for (auto& [s, i] : m) {
vec.push_back(std::make_pair(s, i));
std::cout << vec.back().first << std::endl;
}
}
По сути, вектор создается с помощью элемента карты. Дело в том, что вместо этого он преобразует строку в string_view при вставке в вектор. Я подумал, что это будет нормально, так как мы используем константную ссылку на строку для построения string_view, и строка будет доступна в течение всего срока службы вектора.
Но когда я проверил вывод, он показал, казалось бы, поврежденные данные.
�/�U.�Usfdasdfdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj
�/�U.�Usfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj
@/�U.�Ukdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj
�/�U.�Usfjalskdfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj
@/�U.�Udfjlkdasjfaslkfjaskdljflaskdfjdaslkfjasdlkfjasdlj
.�Usljlasfjlasjfakdlsfjasklfajsklfjaskljlf
asdf
Я предполагаю, что string_view в векторе больше не указывает на правильный элемент. Но почему это произошло? Я предполагал, что unordered_map будет неизменяем во время итерации, поэтому адрес ключевого элемента не изменится.
Ссылка на Godbolt: https://godbolt.org/z/dePdv5
Разве std::make_pair не сделает копию строки, а затем vec сохранит ссылку на копию?
Ааа. Поэтому, когда я исправил это на vec.push_back(std::make_pair(std::string_view(s), i));, оно исправилось. Но мне интересно, как бы это не было поведением по умолчанию?
Работает намного лучше с emplace_back и без make_pair: Live Demo на coliru ;-)
Используя make_pair, вы создаете временный объект типа std::pair<std::string, int>. Я ожидал, что преобразование в std::pair<std::string_view, int> вызовет ошибку, но, похоже, это работает.
В любом случае, std::string_view не принадлежит, поэтому, как только временный объект уничтожается (в конце строки ;), он становится повисшим.
Не стоит заводить новую пару. Попробуйте emplace_back с ключом и значением.
Прохладный. Мне сказали избегать emplace_back, так как это может вызвать неожиданный ctor. Но я думаю, что это единственный случай, когда я нахожу это полезным: P
Преобразование существует, потому что std::string в std::string_view является неявным преобразованием. Вы видите, почему люди были «расстроены» этим решением.
@прохожий серьезно? Думаю, я присоединяюсь к "клубу расстроенных".