The following code:
#include <vector> struct S { int x, y; }; int main() { std::vector<S> v; v.emplace_back(0, 0); }
Gives the following errors when compiled with GCC:
In file included from c++/4.7.0/i686-pc-linux-gnu/bits/c++allocator.h:34:0, from c++/4.7.0/bits/allocator.h:48, from c++/4.7.0/vector:62, from test.cpp:1: c++/4.7.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {int, int}; _Tp = S]': c++/4.7.0/bits/alloc_traits.h:265: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 = S; _Args = {int, int}; _Alloc = std::allocator<S>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]' c++/4.7.0/bits/alloc_traits.h:402:4: required from 'static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>]' c++/4.7.0/bits/vector.tcc:97:6: required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, int}; _Tp = S; _Alloc = std::allocator<S>]' test.cpp:11:24: required from here c++/4.7.0/ext/new_allocator.h:110:4: error: new initializer expression list treated as compound expression [-fpermissive] c++/4.7.0/ext/new_allocator.h:110:4: error: no matching function for call to 'S::S(int)' c++/4.7.0/ext/new_allocator.h:110:4: note: candidates are: test.cpp:3:8: note: S::S() test.cpp:3:8: note: candidate expects 0 arguments, 1 provided test.cpp:3:8: note: constexpr S::S(const S&) test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'const S&' test.cpp:3:8: note: constexpr S::S(S&&) test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'S&&'
Suggesting that vector
is using regular ()
constructor syntax to construct the element from the arguments to emplace_back()
. Why doesn't vector
use the {}
uniform-initialization syntax instead, to make examples like the above work?
It seems to me that there is nothing to lose by using {}
(it calls the constructor when there is one, but still works when there isn't one), and it would be more in the spirit of C++11 to use {}
- after all, the whole point of uniform initialization is that it is used uniformly - that is, everywhere - to initialize objects.
With the simple benchmark here, we notice that emplace_back is 7.62% faster than push_back when we insert 1,000,000 object (MyClass) into an vector. Insert 1,000,000 objects. --- push_back --- push_back takes 0.00665344 seconds.
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.
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.
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.
Great minds think alike ;v) . I submitted a defect report and suggested a change to the standard on this very topic.
http://cplusplus.github.com/LWG/lwg-active.html#2089
Also, Luc Danton helped me understand the difficulty: Direct vs uniform initialization in std::allocator.
When the EmplaceConstructible (23.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.
Altering std::allocator::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back.
std::vector<std::vector<int>> v; v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization
The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail.
I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here.
The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole.
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