Std :: map emplace ends up with an explicit constructor
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
Above is the description by David Stone. I don't understand why the call is emplace_back
an explicit constructor? I don't see anything in the C ++ standard that makes this legal. It was only after listening to David Stone's youtube video, I found out about it.
Now I am trying 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 emplace
doesn't it work? If it emplace_back
can call the constructor explicitly, why emplace
doesn't it do the same?
source to share
emplace
method inserts elements by explicitly calling the constructor with placement new operator
. When entering into a map, you need separate forward arguments to construct the 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
);
source to share
The functions emplace
call your constructor as described in the standard at http://eel.is/c++draft/container.requirements.general#15.5
T
EmplaceConstructible
inX
fromargs
, for zero or more argumentsargs
means that the following expression is well-formed:
allocator_traits<A>::construct(m, p, args)
This means it ultimately comes down to your distributor. Considering the link on what this means we can check http://en.cppreference.com/w/cpp/memory/allocator_traits/construct
We see that if the allocator has no construct member function or if it does std::allocator
, then the call is equivalent to
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
For, std::map<int, A>
type T
in your expression is std::pair<int const, A>
and args...
is 1, 2
. Therefore, to know if your challenge is well-formed emplace
, we just need to decide if the challenge is valid std::pair<int const, A>(1, 2)
. For that, we'll look at the documentation for std::pair
: http://en.cppreference.com/w/cpp/utility/pair/pair
The specified constructor is listed as /*EXPLICIT*/ constexpr pair( const T1& x, const T2& y );
(assuming C ++ 17). In other words, it is like calling a regular function that takes int const &
as the first argument and A const &
as the second. A
is clearly constructive int
, so your call is poorly formed. The call emplace
only saves you from explicit
on any objects that you directly create, which is simple in this case std::pair
, and not any arguments of that type.
source to share
m.insert(std::pair<int, A>(1, 2))
compiles, I don't know why it doesn't compile for you. Maybe you forgot the flag -std=c++11
? This is because the constructor std::pair
explicitly calls the constructor when it copies elements to first
and second
.
If you want to insert in std::map
, you need to specify the key and value in std::pair
. You can use std::make_pair
for this:
m.emplace(std::make_pair(1, 2));
This compiles as the pair will be built in place in the key / value pair and the explicit constructor will be called.
source to share