Reputation: 3074
I'm getting this weird error when inserting an element into std::unordered_map
using emplace
function but not if I use operator[]
function overload. This is my code:
#include <iostream>
#include <unordered_map>
#include <memory>
class B {
public:
B() = default;
~B() = default;
};
class A {
public:
A() = default;
~A() = default;
std::unique_ptr<B> b_ptr;
};
int main() {
std::unordered_map<std::string, A> mp;
// mp.emplace("abc", A()); // gives compiler here
auto& a = mp["def"];
}
I'm getting huge error print when compiled. This is a short note of error: template argument deduction/substitution failed
Upvotes: 6
Views: 1297
Reputation: 71
If you are using C++17 or later, replacing
// mp.emplace("abc", A());
with
mp.try_emplace("abc");
compiles fine.
The reason why mp.emplace("abc", A());
produces a compile error is that the sub-expression A()
constructs a new object of type A
in main()
's scope and so emplace()
expects to directly forward this new object as the argument into some A
constructor, but there is no such constructor (said differently, somewhere inside the emplace()
method's definition, there is something like the code A(A())
, which is undefined).
As mentioned elsewhere, adding a move constructor to class A
with the code A(A&&) = default;
will also fix the compilation error (although this fix might not be an option with other, more complex classes). The reason why this removes the error is because there now exists a constructor that emplace()
can forward this A()
object into (as an argument). That is, the code A(A())
becomes well-defined.
Upvotes: 7
Reputation: 181027
When you use emplace
like mp.emplace("abc", A());
what you are doing is creating a temporary A
, and that object is then copied/moved into the object that emplace
is going to construct. When you did ~A() = default;
in the class, that gets rid of the compiler supplied default move constructor, and the copy constructor is implicitly deleted because std::unique_ptr
can't be copied so the A()
can't be moved or copied into the object emplace
is going to create.
You can fix this by using the std::piecewise_construct
taged version of emplace to forward the parts of the key value pair to emplace
like
mp.emplace(std::piecewise_construct, // call the piecewise_construct overload
std::forward_as_tuple("abc"), // forwards "abc" to the key
std::forward_as_tuple()); // forwards nothing to the value so it can be default constructed
or you could just add a move constructor to A
using
A(A&&) = default;
so that emplace
can move the A()
you created in main
into mp
.
Upvotes: 6