emplace_back() против push_back при вставке пары в std::vector

Я определил следующее

std::vector<std::pair<int,int> > my_vec;
my_vec.push_back( {1,2} ); //this works
my_vec.emplace_back( {1,2} ); // this doesn't work
std::pair<int,int> temp_pair = {1,2}; 
my_vec.emplace_back( temp_pair );         //this works

Я собираю с C++11. Третья строка проблематична, но я думал, что вы можете использовать emplace_back() везде, где у вас есть push_back(), но это, видимо, неправильно. Почему не работает третья строка?

2 ответа

Решение

emplace_back принимает в качестве аргумента пакет с переменными параметрами:

template< class... Args >
reference emplace_back( Args&&... args );

Когда вы называете это так: emplace_back({1, 2}) Вы называете это с одним аргументом, т.е. {1, 2} и Args не может быть выведено. Это из-за того, как язык развивался. В C++ {1, 2} не имеет типа. Это инициализированный список, заключенный в фигурные скобки, и он может использоваться при определенных типах инициализации, но все они требуют, чтобы тип инициализированного был известен. Поэтому temp_pair = {1,2}; работает, потому что тип temp_pair это знать и имеет соответствующий конструктор (int, int),

Тем не мение empalce_back не должен был использоваться таким образом, но вот так:

my_vec.empalce_back(1, 2);

Также обратите внимание, что даже если они работают:

my_vec.emplace_back(std::pair<int, int>{1, 2});
my_vec.emplace_back(temp_pair);   

Они не должны быть использованы. Они не добавляют никакого преимущества перед push_back. Весь смысл emplace_back чтобы избежать создания временного T, Вышеуказанные вызовы все создают временные std::pair<int, int>,


но я думал, что вы можете использовать emplace_back() везде, где у вас есть push_back()

По большей части это правильно. По крайней мере, так было задумано. И вы действительно можете использовать его в своем cese. Вам просто нужно немного изменить синтаксис. Так что вместо push_back({1, 2}) ты можешь использовать emplace_back(1, 2),

Есть ситуация, когда, к сожалению, вы не можете использовать emplace_back: агрегаты.

struct Agg
{
    int a, b;
};

auto test()
{
    Agg a{1, 2}; // ok, aggregate initialization

    std::vector<Agg> v;
    v.emplace_back(1, 2); // doesn't work :(
}

Это не сработает, если вы не добавите конструктор для Agg, Это считается открытым недостатком в стандарте, но, к сожалению, они не могут найти хорошее решение для этого. Проблема в том, как работает инициализация скобки инициализации, и если вы используете ее в общем коде, вы можете пропустить некоторые конструкторы. Все подробности можно найти в этом замечательном посте: почему структура aggreggate может быть инициализирована скобкой, но не может быть использована с использованием того же списка аргументов, что и в инициализации скобки?

1) {1, 2} это не выражение

Синтаксис

{1, 2}

очень "странно" по сравнению с другими вещами в C++.

Обычно в C++ у вас есть выражение (например, x + 1.2) и выражение имеет выведенный тип... например, если x является int переменная тип выражения будет double из-за неявного преобразования intdouble и как работает сложение.

Теперь вернемся к {1, 2}: это "странно", потому что, несмотря на то, что оно выглядит как выражение, оно не... это просто синтаксис, и его значение будет зависеть от того, где оно используется.

В некотором смысле ввод здесь будет работать противоположно большинству мест C++: обычно в C++ это "in"→"out" (тип "выходит" из компонентов), но здесь "out"→"in" (тип is " впрыскивается "в комплектующие).

Текст {1, 2} просто само по себе недостаточно для компиляции (это может означать разные вещи в зависимости от того, где он используется).

Все это сводится к тому, что {1, 2} не может использоваться точно так же, как выражение, даже если правила тщательно разработаны, чтобы заставить вас думать, что это так.

2) emplace_back принимает параметры конструктора

emplace_back был разработан, чтобы иметь возможность создавать объект непосредственно внутри конечного места в контейнере... ожидаемые параметры - это параметры конструктора, и это делается для того, чтобы избежать создания временного объекта просто для того, чтобы иметь возможность сделать копию для конечного пункта назначения и затем выбрасывая это. Ожидаемый параметр emplace_back поэтому 1 а также 2... не единственная вещь, потому что НЕ создание временной единственной вещи - точно причина emplace_back был разработан для.

Вы можете пройти emplace_back экземпляр, поскольку содержащийся тип имеет конструктор копирования, а экземпляр считается параметром для конструктора копирования (перемещения), а не объекта, подлежащего копированию (перемещению) в место назначения (что push_back ожидать). Операции, выполняемые в этом случае, одинаковы, но точка зрения другая.

последствия

Подвести итоги: emplace_back не можете использовать {1, 2} потому что он может принимать что угодно (поэтому не предоставляет достаточно "контекста"), и этот синтаксис не имеет достаточного значения push_back вместо этого может принять его, потому что он ожидает определенного типа, и это обеспечивает достаточный контекст для интерпретации синтаксиса {1, 2}, Это упрощенное объяснение, но, как обычно, C++ пошел в направлении еще большей сложности разбора и особых случаев, поэтому я могу понять, почему вещи для вас не понятны.

Ключевым моментом, однако, является то, что emplace_back не должен был брать полный объект... для этого использования push_back, Новый emplace_back Конструкция должна использоваться, когда вы хотите передать ПАРАМЕТРЫ КОНСТРУКТОРА для построения конечного объекта в контейнере.

Другие вопросы по тегам