当我将元素插入到std::unordered_mapusingemplace函数中时,我遇到了这个奇怪的错误,但如果我使用operator[]函数重载则不会。这是我的代码:
#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"];
}
Run Code Online (Sandbox Code Playgroud)
编译时我收到巨大的错误打印。这是一个简短的错误注释:template argument deduction/substitution failed
小智 7
如果您使用的是 C++17 或更高版本,请替换
// mp.emplace("abc", A());
Run Code Online (Sandbox Code Playgroud)
和
mp.try_emplace("abc");
Run Code Online (Sandbox Code Playgroud)
编译得很好。
产生编译错误的原因mp.emplace("abc", A());是子表达式在的范围内A()构造了一个类型的新对象,因此期望直接将这个新对象作为参数转发到某个构造函数中,但是没有这样的构造函数(换句话说,在某处)在方法的定义中,有类似 code 的内容,它是未定义的)。Amain()emplace() Aemplace()A(A())
class A正如其他地方提到的,在代码中添加移动构造函数A(A&&) = default;也将修复编译错误(尽管对于其他更复杂的类来说,此修复可能不是一个选项)。这消除了错误的原因是因为现在存在一个emplace()可以将此A()对象转发到(作为参数)的构造函数。也就是说,代码A(A())变得定义良好。
当您使用emplace像mp.emplace("abc", A());您所做的那样创建一个临时对象时A,然后将该对象复制/移动到emplace要构造的对象中。当您~A() = default;在类中执行此操作时,就会摆脱编译器提供的默认移动构造函数,并且复制构造函数会被隐式删除,因为std::unique_ptr无法复制,因此无法移动或复制到要创建的A()对象中。emplace
您可以通过使用std::piecewise_constructemplace 的标记版本将键值对的部分转发到emplacelike来解决此问题
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
Run Code Online (Sandbox Code Playgroud)
或者你可以添加一个移动构造函数来A使用
A(A&&) = default;
Run Code Online (Sandbox Code Playgroud)
这样就emplace可以将A()您创建的内容移动main到mp.