Анализатор обнаружил использование функции 'emplace' / 'insert' ассоциативного контейнера стандартной библиотеки ('std::map', 'std::unordered_map'), у которого существует функция 'try_emplace'. Функция 'emplace' / 'insert' может привести к копированию или перемещению аргументов, даже если вставки не произойдёт (элемент с указанным ключом уже присутствует в контейнере). Это может привести к замедлению программы, а в случае перемещения аргумента — к преждевременному освобождению ресурсов.
В зависимости от реализации стандартной библиотеки, функция 'emplace' / 'insert' перед проверкой наличия элемента с указанным ключом может создать временный объект типа 'std::pair', в который аргументы функции будут скопированы или перемещены. Начиная со стандарта С++17, для контейнеров 'std::map' и 'std::unordered_map' была добавлена функция 'try_emplace'. Она гарантирует, что если элемент с указанным ключом уже существует, то аргументы функции не будут скопированы или перемещены.
Рассмотрим пример кода:
class SomeClass { std::string name, surname, descr; public: // User-defined constructor SomeClass(std::string name, std::string surname, std::string descr); // .... }; std::map<size_t, SomeClass> Cont; bool add(size_t id, const std::string &name, const std::string &surname, const std::string &descr) { return Cont.emplace(id, SomeClass { name, surname, descr }) .second; }
В примере в некоторый контейнер 'Cont' производят вставку объекта типа 'SomeClass' по ключу 'id'. Если объект по такому ключу уже ранее был добавлен, могут быть произведены следующие лишние операции:
Используя функцию 'try_emplace' вместо 'emplace', можно избежать лишних операций по формированию временного объекта типа 'std::pair<const size_t, SomeClass>':
bool add(size_t id, const std::string &name, const std::string &surname, const std::string &descr) { return Cont.try_emplace(id, SomeClass { name, surname, descr }) .second; }
Использование 'try_emplace' позволяет также конструировать объекты "по месту" внутри ассоциативного контейнера. В примере тип 'SomeClass' не является агрегатным и содержит определённый пользователем конструктор, поэтому можно избежать также и 3 вызовов конструкторов копирования строк:
bool add(size_t id, const std::string &name, const std::string &surname, const std::string &descr) { return Cont.try_emplace(id, name, surname, descr) .second; }
Начиная с C++20, функция 'try_emplace' работает также и с агрегатными типами:
struct SomeClass { std::string name, surname, descr; }; bool add(size_t id, const std::string &name, const std::string &surname, const std::string &descr) { return Cont.try_emplace(id, name, surname, descr) .second; }