Как использовать std::vector:: emplace_back для vector <vector <int>>?
vector<vector<int> > res;
res.emplace_back({1,2}); // change to res.push_back({1,2}); would work
Это дает мне ошибку
main.cpp:61:25: error: no matching function for call to ‘std::vector<std::vector<int> >::emplace_back(<brace-enclosed initializer list>)’
main.cpp:61:25: note: candidate is:
In file included from /usr/include/c++/4.7/vector:70:0,
from /usr/include/c++/4.7/bits/random.h:34,
from /usr/include/c++/4.7/random:50,
from /usr/include/c++/4.7/bits/stl_algo.h:67,
from /usr/include/c++/4.7/algorithm:63,
from miscalgoc.hpp:1,
from main.cpp:1:
/usr/include/c++/4.7/bits/vector.tcc:92:7: note: void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = std::vector<int>; _Alloc = std::allocator<std::vector<int> >]
Как это сделать? Кроме того, зачем нужен распределитель?
Ответы
Ответ 1
Проблема состоит в том, что аргументы шаблона функции не выводят std::initializer_list
из скопированного-init-list (например, { 1, 2 }
).
Пример:
#include <initializer_list>
#include <type_traits>
template<typename T>
void func(T arg) {
}
int main() {
auto init_list = {1, 2}; // This works because of a special rule
static_assert(std::is_same<decltype(init_list), std::initializer_list<int>>::value, "not same");
func(std::initializer_list<int>{1, 2}); // Ok. Has explicit type.
func({1, 2}); // This doesn't because there no rule for function
// template argument to deduce std::initializer_list
// in this form.
}
Живой пример
std::vector::emplace_back()
- это шаблон функции с выведенными аргументами. Таким образом, передача {1, 2}
не будет работать, потому что она не может его вывести. Ввод явного типа в него
res.emplace_back(std::initializer_list<int>{1,2});
заставит его работать.
Живой пример
Ответ 2
@Марк ответ довольно правильный. Теперь давайте рассмотрим более практичный случай. После некоторых правильных операций вы собрали некоторые данные с помощью vector<int>
и хотите вставить их в vector<vector<int>>
:
std::vector<std::vector<int>> res;
for (int i = 0; i < 10000; ++i) {
//
// do something
//
std::vector<int> v(10000, 0); // data acquired
res.push_back(v);
}
Это не похоже на присвоение значений, которые вы уже знаете. Использование std::initializer_list
, вероятно, больше не является решением. В таких случаях вы можете использовать std::move
(допустимо либо emplace_back
, либо push_back
)
for (int i = 0; i < 10000; ++i) {
std::vector<int> v(10000, 0); // will become empty afterward
res.emplace_back(std::move(v)); // can be replaced by
// res.push_back(std::move(v));
}
Производительность более или менее улучшена. Вы все еще можете извлечь выгоду из концепции xvalue Move-Insert-вставки, конструирующей объекты с помощью Move-Constructor, а не копируя.
UPDATE
причина того, что res.push_back(move(v))
работает, заключается в том, что они перегружают метод std::vector::push_back(value_type&& val)
после С++ 11. Это сделано для поддержки ссылки rvalue сознательно.
Ответ 3
Посмотрите документацию для vector::emplace_back
. emplace_back
пытается создать новый элемент в вашем векторе, вызывая конструктор для нового элемента с переданными аргументами. Таким образом, в принципе, когда вы вызываете emplace_back({1,2})
, он пытается передать {1,2}
in в конструктор, но так как res
- вектор векторов ints, он смотрит на конструкторы векторов, ни один из которых не может принимать список инициализаторов, заключенных в фигурные скобки.
Кроме того, посмотрите документацию для vector::push_back
. Когда вызывается push_back
, он создает объект по умолчанию (в данном случае вектор ints) и копирует в него значения. Я бы предположил, что причина, по которой работает push_back({1,2})
, заключается в том, что список инициализаторов, заключенный в скобки, создает тип значения, который push_back
принимает.