class A {
public:
explicit A(int x) {}
};
vector<A> v;
v.push_back(1); // compiler error since no implicit constructor
v.emplace_back(1); // calls explicit constructor
The above is from a video by David Stone. What I fail to understand is why does emplace_back
call
the explicit constructor? I do not see anything in the C++ standard that
makes this legit. Only after listening to David Stone's youtube video,
I found out about this.
Now, I try the same with std::map
.
map<int, A> m;
m.insert(pair<int, A>(1, 2)); // compiler error since no implicit constructor
m.emplace(1, 2); // compiler error since no implicit constructor
Why does emplace
fail here ? If emplace_back
can call explicit
constructor, why doesn't emplace
do the same ?
emplace
method inserts elements by explicitly calling constructor with placement new operator
. While emplacing into map you need separately forward arguments for constructing key and value.
m.emplace
(
::std::piecewise_construct // special to enable forwarding
, ::std::forward_as_tuple(1) // arguments for key constructor
, ::std::forward_as_tuple(2) // arguments for value constructor
);
The emplace
functions invoke your constructor as described in the standard at http://eel.is/c++draft/container.requirements.general#15.5
T
isEmplaceConstructible
intoX
fromargs
, for zero or more argumentsargs
, means that the following expression is well-formed:
allocator_traits<A>::construct(m, p, args)
This means that it ultimately comes down to your allocator. Looking at the reference for what that calls means, we can check http://en.cppreference.com/w/cpp/memory/allocator_traits/construct
We see that if the allocator does not have a construct member function, or if it is std::allocator
, then the call is equivalent to
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
For std::map<int, A>
, the type T
in your expression is std::pair<int const, A>
and args...
is 1, 2
. So to know whether your call to emplace
is well-formed, we just need to decide whether a call to std::pair<int const, A>(1, 2)
is valid. So for that, we look at the documentation for std::pair
: http://en.cppreference.com/w/cpp/utility/pair/pair
The constructor in question is listed as /*EXPLICIT*/ constexpr pair( const T1& x, const T2& y );
(assuming C++17). In other words, it is just like calling a regular function that accepts int const &
as the first argument and and A const &
as the second. A
is only explicitly constructible from int
, so your call is ill-formed. The emplace
call is only saving you from explicit
on any objects you are directly constructing, which in this case is just std::pair
, not any arguments to that type.
m.insert(std::pair<int, A>(1, 2))
compiles, I don't know why it doesn't compile for you. Maybe forgot -std=c++11
flag? That's because the std::pair
constructor explicitly calls the constructor when it copies the elements into first
and second
.
If you want to emplace into a std::map
, you have to specify a key and a value in a std::pair
. You can use std::make_pair
for that:
m.emplace(std::make_pair(1, 2));
This compiles, as the pair will get constructed in place in the key/value pair, and the explicit constructor will be called.
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