Оптимальное создание нового пустого элемента в std :: map - PullRequest
0 голосов
/ 15 сентября 2018

Для вставки новых элементов std::map требуется наличие std::pair объекта. Я не нашел альтернативного метода добавления нового элемента в std::map без создания такого std::pair объекта.

Но предположим, что значение std::map является тяжелым объектом, потребляющим много ресурсов. Вставка такого элемента в std::map ограничивает одну ненужную копию. В этом случае У меня есть некоторый объект X, который нужно было скопировать непосредственно в std::map, авторы std вынуждают меня выполнить эту копию в два этапа:

  1. копия X -> std::pair
  2. скопировать пару obj -> std::map вставить местоположение.

Как устранить эти ненужные копии? Реализация конструктора Move не решает мою проблему - копия все еще существует.

Я подозреваю, что какой-то метод в std::map вставляет новый пустой элемент с некоторым ключом, но без значения, без вызова какого-либо конструктора для значения. Тогда я могу перенести значение на свой собственный путь. Но я не нахожу такой метод в std::map!

Ответы [ 2 ]

0 голосов
/ 15 сентября 2018

значение std::map является тяжелым объектом, потребляющим много ресурсов.Вставка такого элемента в std::map ограничивает одну ненужную копию.Как устранить эти ненужные копии?

Вы правы в этом, и std::map::emplace - единственное решение, которое мы имеем в настоящее время.Небольшое объяснение использования std::map::emplace из cppreference.com приводится ниже:

Тщательное использование emplace позволяет создавать новый элемент, избегая при этом ненужные операции копирования или перемещения .Конструктор нового элемента (т. Е. std::pair<const Key, T>) вызывается с точно такими же аргументами, что и для emplace , пересылаемыми через std::forward<Args>(args)....

Это означает, что экземпляры класса a могут быть созданы на месте во время вставки карты (т. Е. вместо создания и копирования ) через std::map::emplace, когда вы предоставляетеподрядчик в классе с точно такими же аргументами.И затем вам нужно использовать std::map::emplace вместе с std::piecewise_construct и std::forward_as_tuple (при условии, что класс содержит большечем один член).

myMap.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(/*key of map*/),
        std::forward_as_tuple(/*all the members which you want to constrct in place*/)
    );

Чтобы продемонстрировать приведенный выше случай, я создал небольшой пример кода, в котором элементы ClassA будут построены на месте без вызова какой-либо специальной функции-члена.Чтобы убедиться, я отключил конструкторы default, copy и move.

СМОТРИТЕ ЗДЕСЬ ЗДЕСЬ

#include <iostream>
#include <map>
#include <tuple>

class ClassA
{
    int _val;
    std::string _str;
public:
    explicit ClassA(const int val, const std::string& str) : _val(val), _str(str)
    {
        std::cout << "A class: C'tor called...!\n";
    }
    // disable the following
    ClassA() = delete;
    ClassA(const ClassA&) = delete;
    ClassA& operator=(const ClassA&) = delete;
    ClassA(ClassA&&) = delete;
    ClassA& operator=(ClassA&&) = delete;

};

class ClassB
{
    ClassA _aObj;
public:
    explicit ClassB(const int val, const std::string& str) : _aObj(val, str)
    {
        std::cout << "B class: C'tor called...!\n";
    }
    // disable the following
    ClassB() = delete;
    ClassB(const ClassB&) = delete;
    ClassB& operator=(const ClassB&) = delete;
    ClassB(ClassB&&) = delete;
    ClassB& operator=(ClassB&&) = delete;
};

int main()
{
    std::map<int, ClassB> myMap;
    myMap.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(1),
        std::forward_as_tuple(1, "some string")
    );
    return 0;
}

Вывод:

A class: C'tor called...!
B class: C'tor called...!

Обновление : С другой стороны,

Элемент может быть сконструирован даже если там уже есть элемент с ключом в контейнере, в этом случае вновь созданный элемент будет немедленно уничтожен.

Это означает, что вы ожидаете(или предположение):

" Я подозреваю, что какой-то метод в std::map вставляет новый пустой элемент с некоторым ключом , но нетзначение , без вызова какого-либо конструктора для значения. Затем я могу переместить значение в свой собственный путь. "

- невозможно достичь вышеупомянутым std::map::emplace способом.Как отметил @aschepler в комментариях, вы можете иметь карты с иногда (необязательными) ключами и без значений, используя функцию C ++ 17 std::optional.

Для этого вам нужно сделать значения необязательными, как указано ниже, и, конечно, версия компилятора поддерживает C ++ 17 или новее.

std::map<Key, std::optional<Value>> myMap;

Теперь вы можете создавать объекты в любое время в коде и послекакое-то время вы можете переместить его в соответствующее значение ключа (т. е. запись).И последнее, но не менее важное: не забудьте указать стандартные значения для перемещения

СМ. ОБРАЗЕЦ КОДА ЗДЕСЬ

#include <optional>

class ClassA {
    /* same as before*/
public:
    // disable the copy c'tor
    // enable move c'ntors
    ClassA(ClassA&&) = default;
    ClassA& operator=(ClassA&&) = default;

};

class ClassB {
    /* same as before*/
public:
    // disable the copy c'tor
    // enable move c'ntors
    ClassB(ClassB&&) = default;
    ClassB& operator=(ClassB&&) = default;
};

int main() {
    std::map<int, std::optional<ClassB>> myMap;
    // created without calling any constructors
    myMap.emplace(1, std::nullopt);
    //later in the code
    ClassB bObj{1, "JeJo"};
    // again after..... move it to the required key-value
    myMap[1] = std::move(bObj);
    return 0;
}

Выход:

A class: C'tor called...!
B class: C'tor called...!
0 голосов
/ 15 сентября 2018
...