Consider the following program:
#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"); }
It doesn't work:
$ 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
What is the correct way to do this and why?
(Also tried single and double braces)
C++ Vector Library - emplace_back() Function The C++ function std::vector::emplace_back() inserts new element at the end of vector. Reallocation happens if there is need of more space. This method increases container size by one.
Specific use case for emplace_back : If you need to create a temporary object which will then be pushed into a container, use emplace_back instead of push_back . It will create the object in-place within the container. Notes: push_back in the above case will create a temporary object and move it into the container.
because emplace_back would construct the object immediately in the vector, while push_back , would first construct an anonymous object and then would copy it to the vector.
Calling emplace_back will call the move constructor of std::string when std::move is used, which could save on a copy (so long as that string isn't stored in a SSO buffer). Note that this is essentially the same as push_back in this case.
You need to explicitly define a ctor for the class:
#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"); }
The point of using emplace_back
is to avoid creating a temporary object, which is then copied (or moved) to the destination. While it is also possible to create a temporary object, then pass that to emplace_back
, it defeats (at least most of) the purpose. What you want to do is pass individual arguments, then let emplace_back
invoke the ctor with those arguments to create the object in place.
For anyone from the future, this behavior will be changed in C++20.
In other words, even though implementation internally will still call T(arg0, arg1, ...)
it will be considered as regular T{arg0, arg1, ...}
that you would expect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With