unordered_map и emplace, почему ctor вызывается дважды? - PullRequest
2 голосов
/ 27 января 2020

Вот пример кода:

struct T
{
    T(int x) : x_(x)
    {}

    T(T&&) = delete;
    T(const T&) = delete;

    int x_;
};

int main()
{
    std::unordered_map<int, T> m;
    m.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(2));
    m.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(2));

    return 0;
}

Второе emplace завершается неудачно, но конструктор T вызывается дважды. У меня есть хотя, что emplace будет создавать объект только тогда, когда вставка возможна. Не могли бы вы объяснить это?

Редактировать: я использую компилятор Visual Studio 2017.

1 Ответ

4 голосов
/ 27 января 2020

From cppreference :

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

Причиной такого поведения является то, что контейнер должен создать ключевой объект, чтобы иметь возможность проверить, присутствует ли он уже; сопоставленный объект должен быть создан одновременно, поскольку они являются членами одного и того же объекта (пара value_type).

try_emplace (поскольку C ++ 17) является лучший вариант в этом случае, так как он будет создавать сопоставленный объект только в случае успешной вставки. Это можно сделать, поскольку он принимает ключ в качестве первого аргумента и заменяет сопоставленный объект из оставшихся аргументов, что приводит к гораздо более приятному интерфейсу:

m.try_emplace(1, 2);
m.try_emplace(1, 2);
              ^ key (copied or moved)
                 ^ mapped_type emplacement args (forwarded)
...