C++11 emplace обратно на вектор?
рассмотрим следующую программу:
#include <string>
#include <vector>
using namespace std;
struct T
{
int a;
double b;
string c;
};
vector<T> V;
int main()
{
V.emplace_back(42, 3.14, "foo");
}
не работает:
$ g++ -std=gnu++11 ./test.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from ./test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = T; _Args = {int, double, const char (&)[4]}; _Tp = T]’:
/usr/include/c++/4.7/bits/alloc_traits.h:253:4: required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.7/bits/alloc_traits.h:390:4: required from ‘static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>]’
/usr/include/c++/4.7/bits/vector.tcc:97:6: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, double, const char (&)[4]}; _Tp = T; _Alloc = std::allocator<T>]’
./test.cpp:17:32: required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: no matching function for call to ‘T::T(int, double, const char [4])’
/usr/include/c++/4.7/ext/new_allocator.h:110:4: note: candidates are:
./test.cpp:6:8: note: T::T()
./test.cpp:6:8: note: candidate expects 0 arguments, 3 provided
./test.cpp:6:8: note: T::T(const T&)
./test.cpp:6:8: note: candidate expects 1 argument, 3 provided
./test.cpp:6:8: note: T::T(T&&)
./test.cpp:6:8: note: candidate expects 1 argument, 3 provided
Как правильно это сделать и почему?
(также пробовал одинарные и двойные скобки)
6 ответов:
можно использовать
{}
синтаксис для инициализации нового элемента:V.emplace_back(T{42, 3.14, "foo"});
это может быть или не быть оптимизировано, но это должно быть.
вы должны определить конструктор для этого, чтобы работать, обратите внимание, что с вашим кодом вы даже не можете сделать:
T a(42, 3.14, "foo");
но это то, что вам нужно иметь emplace работу.
так же:
struct T { ... T(int a_, double b_, string c_) a(a_), b(b_), c(c_) {} }
будет работать желаемым образом.
вы должны явно определить конструктор для класса:
#include <string> #include <vector> using namespace std; struct T { int a; double b; string c; T(int a, double b, string &&c) : a(a) , b(b) , c(std::move(c)) {} }; vector<T> V; int main() { V.emplace_back(42, 3.14, "foo"); }
использовать
emplace_back
- это чтобы не создавать временный объект, который затем копируется (или перемещается) в пункт назначения. Хотя также можно создать временный объект, а затем передать его вemplace_back
, он побеждает (по крайней мере, большую часть) цели. То, что вы хотите сделать, это передать отдельные аргументы, а затем пустьemplace_back
вызовите ctor с этими аргументами, чтобы создать объект на месте.
конечно, это не ответ, но показывает интересную особенность кортежей:
#include <string> #include <tuple> #include <vector> using namespace std; using T = tuple < int, double, string >; vector<T> V; int main() { V.emplace_back(42, 3.14, "foo"); }
Если вы не хотите (или не можете) добавить конструктор, специализируйте распределитель для T (или создайте свой собственный распределитель).
namespace std { template<> struct allocator<T> { typedef T value_type; value_type* allocate(size_t n) { return static_cast<value_type*>(::operator new(sizeof(value_type) * n)); } void deallocate(value_type* p, size_t n) { return ::operator delete(static_cast<void*>(p)); } template<class U, class... Args> void construct(U* p, Args&&... args) { ::new(static_cast<void*>(p)) U{ std::forward<Args>(args)... }; } }; }
Примечание: конструкция функции-члена, показанная выше, не может компилироваться с clang 3.1 (Извините, я не знаю, почему). Попробуйте следующий, если вы будете использовать clang 3.1 (или другие причины).
void construct(T* p, int a, double b, const string& c) { ::new(static_cast<void*>(p)) T{ a, b, c }; }
Это, кажется, рассматривается в 23.2.1 / 13.
во-первых, определения:
данный тип контейнера х имея allocator_type идентичны и типа value_type идентичны T и учитывая, именующее м типа а, указатель п типа T*, выражение в типа T, и R-значения РВ типа Т, определены следующие термины.
теперь, что делает его emplace-конструктивным:
T является EmplaceConstructible в X от аргументы , ноль или больше аргументы args, означает, что следующее выражение хорошо сформировано: allocator_traits:: construct(m, p, args);
и, наконец, примечание о реализации по умолчанию вызова конструкции:
Примечание: контейнер вызывает allocator_traits:: construct (m, p, args) to построить элемент в точке p с помощью args. Конструкция по умолчанию в с std::распределитель вызовов ::новая((недействительными*)Р) Т(аргументы), но специализированные распределители могут выбрать различное определение.
Это в значительной степени говорит нам, что для схемы распределителя по умолчанию (и потенциально единственной) вы должны определили конструктор с правильным количеством аргументов для вещи, которую вы пытаетесь emplace-построить в контейнер.
вы должны определить конструктор для вашего типа
T
потому что он содержитstd::string
что не тривиально.кроме того, было бы лучше определить (возможно по умолчанию) перемещение ctor/assign (потому что у вас есть подвижный
std::string
как член) - это поможет продвинуть вашT
гораздо эффективнее...или, просто использовать
T{...}
для вызова перегруженныхemplace_back()
как рекомендовано в ответе соседа... все зависит от ваших типичных случаев использования...