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
из-за неявного преобразования int
→ double
и как работает сложение.
Теперь вернемся к {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
Конструкция должна использоваться, когда вы хотите передать ПАРАМЕТРЫ КОНСТРУКТОРА для построения конечного объекта в контейнере.