Как избежать временных копий при использовании emplace для добавления в std::map?

#include <iostream>
#include <map>
using namespace std;

struct FooStruct 
{
    int a;
    int b;
};

int main()
{
    map<int, FooStruct> fooMap;
    fooMap.emplace<int, FooStruct>(0, {1, 2});
    return 0;
}

С точки зрения предотвращения временных копий, является ли приведенное выше правильным использованием emplace? Является ли вышеуказанная форма лучше, чем

fooMap.emplace(make_pair<int, FooStruct>(0, {1, 2}));

Или эти формы эквивалентны, и обе они избегают создания временной копии FooStruct?


person DigitalEye    schedule 03.08.2017    source источник
comment
Разница в том, что make_pair использует семантику конструктора перемещения pair, тогда как другим способом вы вызываете конструктор шаблона.   -  person Swift - Friday Pie    schedule 03.08.2017
comment
А как насчет fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(1, 2)); ?   -  person max66    schedule 03.08.2017
comment
@max66: Вы говорите, что ваша форма лучше, чем у двух упомянутых выше? если да, то не могли бы вы объяснить, почему это так?   -  person DigitalEye    schedule 04.08.2017
comment
Нет; Я только показываю вам другой способ сделать то же самое; если я не ошибаюсь, это (примерно) то же самое, что и в первой форме, но без необходимости явного указания типов шаблонов (что может быть хорошо в некоторых обстоятельствах).   -  person max66    schedule 04.08.2017
comment
@ max66: +1 за устранение необходимости указывать типы шаблонов.   -  person DigitalEye    schedule 04.08.2017


Ответы (2)


Если вы определяете «правильность» как краткость, вы можете использовать std::map::insert вместо std::map::emplace следующим образом:

fooMap.insert({0, {1, 2}});

С emplace вам нужно будет либо явно указать типы, как в вашем примере, либо явно определить конструктор в FooStruct в случае, предложенном @max66:

fooMap.emplace(std::piecewise_construct,
    std::forward_as_tuple(0),
    std::forward_as_tuple(1, 2));

(что также не хватает краткости).

fooMap.insert({0, {1, 2}}); не должно отличаться от

fooMap.emplace(make_pair<int, FooStruct>(0, {1, 2}));

с точки зрения количества созданных объектов, поскольку он также использует конструктор перемещения std::pair, как указал @Swift.

Если «правильный» означает «компилируемый и работает как положено во время выполнения», то оба ваших примера верны.

person Dev Null    schedule 05.08.2017
comment
Хорошо спасибо. На самом деле мне нужен был единственный экземпляр FooStruct, который создается непосредственно в памяти карты. fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(1, 2)); выполняет именно это. Другие формы, лаконичные или нет, предполагают создание временного экземпляра FooStruct, чего я хочу избежать. - person DigitalEye; 06.08.2017
comment
Не могли бы вы обновить свой вопрос, чтобы отразить, что вы искали минимальное количество построенных объектов? Кроме того, эта форма emplace может давать незначительные преимущества в производительности во время выполнения или вообще не давать их, поскольку компиляторы могут оптимизировать все различия, и вам может потребоваться сравнить сборку. - person Dev Null; 07.08.2017

РЕДАКТИРОВАТЬ:

Из трех форм, обсуждаемых в этой теме, та, которая позволяет избежать ненужных копий, — это форма, предложенная @max66. Следующий код и его вывод фиксируют эти три формы в действии.

#include <iostream>
#include <map>
using namespace std;

struct FooStruct 
{
    FooStruct() 
    { 
        cout << "FooStruct Default Constructor" << endl; 
    }
    FooStruct(const FooStruct& other) 
    {
        this->a = other.a;
        this->b = other.b;
        cout << "FooStruct Copy Constructor" << endl;
    }
    FooStruct(int a, int b)
    {
        this->a = a;
        this->b = b;
        cout << "FooStruct Parametrized Constructor" << endl;
    }
    int a;
    int b;
};

Выход:

foo.emplace<int, FooStruct>(0, {1, 2})
FooStruct Parametrized Constructor
FooStruct Copy Constructor
fooMap.emplace(make_pair<int, FooStruct>(1, { 2, 3 }))
FooStruct Parametrized Constructor
FooStruct Copy Constructor
FooStruct Copy Constructor
fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(2), std::forward_as_tuple(2, 4))
FooStruct Parametrized Constructor

============

ОРИГИНАЛ (НЕПРАВИЛЬНО)

Я был немного ленив и не пытался копать глубже, прежде чем публиковать вопрос. Теперь я вижу, что все эти три формы (третья форма взята из комментария @max66) эквивалентны в том смысле, что все три из них избегают создания временной копии FooStruct.

#include <iostream>
#include <map>
using namespace std;

struct FooStruct 
{
    FooStruct() { cout << "FooStruct Default Constructor" << endl; }
    FooStruct(int a, int b) { this->a = a; this->b = b; cout << "FooStruct Parametrized Constructor" << endl; }
    int a;
    int b;
};

int main()
{
    map<int, FooStruct> fooMap;
    fooMap.emplace<int, FooStruct>(0, {1, 2});
    fooMap.emplace(make_pair<int, FooStruct>(1, { 2, 3 }));
    fooMap.emplace(std::piecewise_construct, std::forward_as_tuple(2), std::forward_as_tuple(2, 4));
    return 0;
}

Приведенный выше код (созданный с помощью Visual C++ 2015) выдает следующий результат:

FooStruct Parametrized Constructor
FooStruct Parametrized Constructor
FooStruct Parametrized Constructor

PS: я проверил, что каждая строка в приведенном выше выводе соответствует одному вызову emplace выше.

person DigitalEye    schedule 04.08.2017
comment
Я не согласен с тем, что все эти три формы [...] эквивалентны в том, что все три избегают создания временной копии FooStruct. Это верно для первой и третьей формы; в случае второй формы, насколько я знаю, происходит создание временной копии пары int/FooStruct, которая используется для построения пары в карте с помощью конструктора копирования (конструктор перемещения, начиная с C++11) . - person max66; 04.08.2017
comment
@max66: Ты прав. Я забыл о конструкторе копирования и, очевидно, сделал неверный вывод. Итак, теперь мы знаем, что ваша форма с использованием forward_as_tuple — лучшее решение. Я отредактирую свой ответ, чтобы отразить этот вывод. - person DigitalEye; 05.08.2017