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?

提问人:Doot 提问时间:10/21/2020 更新时间:10/21/2020 访问量:471

问:

下面是一个最小示例,其中类型的对象包含 .地图中唯一会改变的是值,而不是长度,也不是键。WrapMapunordered_map

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

#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   

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

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

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

我怎样才能使每个密钥永远不会被复制(或至少最小化它?

相关新闻: 插入无序地图调用构造函数

C++ 复制 复制构造函数 move-semantics

评论


答:

1赞 dfrib 10/21/2020 #1

EMPLACE公司

您可以使用以下的 emplace 成员函数:unordered_map

如果容器中没有具有键的元素,则将新元素插入到使用给定参数就地构造的容器中。

谨慎使用 emplace 可以构造新元素,同时避免不必要的复制或移动操作。新元素(即 )的构造函数被调用,其参数与提供给 emplace 的参数完全相同,通过 转发。[...]std::pair<const Key, T>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,如果密钥已存在,则允许保留传递给它的给定资源:

[...]与 or 不同,如果不发生插入,这些函数不会从右值参数中移动,这使得操作值为仅移动类型的映射变得容易,例如 。此外,将键和参数分别处理mapped_type,这与需要参数来构造 a (即 a ) [...] 不同。insertemplacestd::unordered_map<std::string, std::unique_ptr<foo>>try_emplaceemplace,value_typestd::pair

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

评论

0赞 Doot 10/21/2020
对于许多对和几张地图来说,这似乎很麻烦,但它工作得很好!+1