# V823. Decreased performance. Object may be created in-place in a container. Consider replacing methods: 'insert' -> 'emplace', 'push_*' -> 'emplace_*'.

Анализатор обнаружил потенциальное использование менее эффективного метода. При вставке временного объекта в контейнер при использовании методов 'insert' / 'push_*' он конструируется снаружи контейнера и затем перемещен/скопирован внутрь контейнера.

Методы 'emplace' / 'emplace_*' в свою очередь позволяют устранить один избыточный вызов конструктора перемещения / копирования и создают объект "по месту" внутри контейнера, идеально передавая параметры функции конструктору объекта.

Анализатор предлагает следующие замены:

- insert -> emplace
- insert_after -> emplace_after
- push_back -> emplace_back
- push_front -> emplace_front

Пример кода:

    std::string str { "Hello, World" };
    std::vector<std::string> vec;
    std::forward_list<std::string> forward_list;
    std::list<std::string> list;
    std::map<std::string, std::string> map;

    ....

    vec.push_back(std::string { 3, 'A' });
    forward_list.push_front(std::string { str.begin(), str.begin() + 6 });
    list.push_front(str.substr(7));
    list.push_back(std::string { "Hello, World" });
    map.insert(std::pair<std::string, std::string> { "Hello", "World" });

Оптимизированный код:

    std::vector<std::string> vec;
    std::forward_list<std::string> forward_list;
    std::list<std::string> list;
    std::map<std::string, std::string> map;

    ....

    vector.emplace_back(3, 'A');
    forward_list.emplace_front(string.begin(), string.begin() + 6);
    list.emplace_front(str.begin() + 7, str.end());
    list.emplace_back("Hello, World");
    map.emplace("Hello", "World");

В некоторых случаях такая замена может привести к потере базовой гарантии безопасности исключений. Рассмотрим пример:

    std::vector<std::unique_ptr<int>> vectUniqP;
    vectUniqP.push_back(std::unique_ptr<int>(new int(0)));
    auto *p = new int(1);
    vectUniqP.push_back(std::unique_ptr<int>(p));

В таком случае замена 'push_back' на 'emplace_back' может привести к утечке памяти, если 'emplace_back' бросит исключение в связи с отсутствием памяти. Анализатор не выдает срабатывания на подобные случаи и не предлагает замену. Если же код был изменен на ошибочный, анализатор выдаст предупреждение V1023.

Иногда замена вызовов методов 'insert' / 'push_*' на их аналог 'emplace' / 'emplace_*' не принесет оптимизации:

    std::string foo()
    {
      std::string res;
      // doing some heavy stuff
      return res;
    }

    std::vector<std::string> vec;
    ....
    vec.push_back(foo());

В этом примере метод 'emplace_back' будет иметь такую же эффективность, что и вставка элемента через 'push_back'. Однако, предупреждение всё равно выдаётся для единообразия. Во всех подобных ситуациях рационально сделать замену, чтобы код смотрелся единообразно и при его просмотре не приходилось каждый раз задумываться, стоит использовать 'emplace*' или нет. Если пользователь не согласен с таким подходом, он может рассматривать подобные предупреждения как ложные и подавлять их.

Примечание. К описанной здесь рекомендации стоит подходить разумно, а не формально. Например, замена

    widgets.push_back(Widget(foo, bar, baz));
    // на
    widgets.emplace_back(Widget(foo, bar, baz));

не даёт никакого выигрыша в скорости работы программы. Более того, использование 'emplace_back' может замедлить скорость компиляции кода. Подробнее эта тема освящена в статье "Don't blindly prefer emplace_back to push_back". Наша команда выражает Arthur O'Dwyer благодарность за эту публикацию.