C ++:如何将键和值传递到构造函数中以制作地图而不在构造期间复制地图的值?

C++: How can I pass keys and values into a constructor to make a map without copying the values of the map during construction?

这是一个最小的示例,其中 WrapMap 类型的对象包含 unordered_map。映射中唯一会改变的是值,而不是长度和键。

但是,我发现传递给每一对的每个值都被复制了两次。 通过使用 move,它似乎将副本数减少了 1(虽然移动没有显示在输出中,所以可能我做错了什么)。

#include <iostream>
#include <unordered_map>

using std::cout;

struct MyStruct {
    int x;
    MyStruct(int x) : x(x) { cout << "Constructed " << this << " from " << x << "\n"; }
    MyStruct(const MyStruct& from) : x(from.x) { cout << "Copied      " << this << " from " << &from << "\n"; }
    MyStruct(MyStruct&& from) : x(from.x) { cout << "Moved       " << this << " from " << &from << "\n"; }
    ~MyStruct() { cout << "Destructed  " << this << " from " << x << "\n"; }
};

struct WrapMap {
    std::unordered_map<std::string, MyStruct>&& my_map;
    WrapMap(std::unordered_map<std::string, MyStruct>&& kv)
        : my_map(std::move(kv)) {
        /*
        // Just to make sure it inputs the values correctly
        cout << "{";
        for (auto it = my_map.begin(); it != my_map.end(); ++it) {
            if (it != my_map.begin()) cout << ", ";
            cout << it->first << ": MyStruct " << it->second.x;
        }
        cout << "}\n";
        */
    }
};

int main() {
    WrapMap object({
        {"foo", 2},
        // several pairs
    });
}
Constructed 0x7ffd76fadbb8 from 2                                                                                               
Copied      0x2611c80 from 0x7ffd76fadbb8                                                                                       
{foo: MyStruct 2}                                                                                                               
Destructed  0x7ffd76fadbb8 from 2                                                                                               
Destructed  0x2611c80 from 2   

我的假设是长指针指向 const 内存(只是一个猜测),因此它必须将每个元素从 const 内存复制到非常量内存。

我曾尝试使用 initializer_list<pair<string, MyStruct>>,但无法将其转换为 unordered_map

std::unordered_map<std::string, MyStruct> object = { {"foo", 2} } 似乎为每个值调用复制构造函数。

我怎样才能让每个键都不会被复制(或者至少最小化?)

相关:

安置

可以使用unordered_mapemplace成员函数:

Inserts a new element into the container constructed in-place with the given args if there is no element with the key in the container.

Careful use of emplace allows the new element to be constructed while avoiding unnecessary copy or move operations. The constructor of the new element (i.e. std::pair<const Key, T>) is called with exactly the same arguments as supplied to emplace, forwarded via std::forward<Args>(args).... [...]

std::unordered_map<std::string, MyStruct> m;
m.emplace(std::make_pair("foo", 2));

C++17:try_emplace

从 C++17 开始,您还可以使用 try_emplace,如果键已经存在,它允许保留传递给它的给定资源:

[...] Unlike insert or emplace, these functions do not move from rvalue arguments if the insertion does not happen, which makes it easy to manipulate maps whose values are move-only types, such as std::unordered_map<std::string, std::unique_ptr<foo>>. In addition, try_emplace treats the key and the arguments to the mapped_type separately, unlike emplace, which requires the arguments to construct a value_type (that is, a std::pair) [...].

std::unordered_map<std::string, MyStruct> m;
m.try_emplace("foo", 2);