使用 emplace 函数插入时 std::unordered_map 给出错误

Har*_*rry 6 c++

当我将元素插入到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())变得定义良好。


Nat*_*ica 6

当您使用emplacemp.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()您创建的内容移动mainmp.

  • @ALX23z 虽然添加移动构造函数将使原始代码正常工作,并且我已经添加了它,但这样做可能会导致性能损失。它必须执行默认构造,然后进行移动,其中使用“piecewise_construct”,我们将所有参数转发到“emplace”,并且只有一个默认构造函数调用。在这种情况下,这可能并不重要,但我可以看到如果班级有很多成员需要移动,情况会怎样。 (5认同)