地图的密钥是否可以与部分值共享?

Can a map's key be shared with part of the value?

本文关键字:共享 密钥 是否 地图      更新时间:2023-10-16

std::mapstd::unordered_map的密钥可以与部分值共享吗?特别是如果密钥不是平凡的,比如像std::string

作为一个简单的例子,让我们以 Person 对象为例:

struct Person {
    // lots of other values
    std::string name;
}
std::unordered_map<std::string, std::shared_ptr<Person>> people;
void insertPerson(std::shared_ptr<Person>& p) {
    people[p.name] = p;
    //    ^^^^^^
    //    copy of name string
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
    return people[name];
}

我的第一个想法是围绕指向该人的名字的包装器,但我无法弄清楚如何按名称进行查找。

就您的目的而言,std::map可以被视为包含std::pairstd::set,该根据对的第一个元素进行排序(因此可以有效地访问(。

如果键元素和值元素部分相同,则此视图特别有用,因为这样就不需要人为地将集合的值和键元素分开(也不需要围绕选择键的值编写包装器(。

相反,只需要提供一个自定义排序函数,该函数适用于集并提取相关关键部分。

按照这个想法,你的例子变成了

auto set_order = [](auto const& p, auto const& s) { return p->name < s->name; };
std::set<std::shared_ptr<Person>, decltype(set_order)> people(set_order);
void insertPerson(std::shared_ptr<Person>& p) {
    people.insert(p);
}

作为替代方案,您也可以在此处删除自定义比较并按共享指针中的地址对集合进行排序(支持<,因此可以直接在集合中使用(:

std::set<std::shared_ptr<Person> > people;
void insertPerson(std::shared_ptr<Person>& p) {
    people.insert(p);
}

在需要时用unordered_set替换set(通常您还需要提供合适的哈希函数(。

编辑:可以使用std:lower_bound执行查找:

std::shared_ptr<Person> lookupPerson(std::string const& s)
{
    auto comp =  [](auto const& p, auto const& s) { return p->name < s; };
    return *std::lower_bound(std::begin(people), std::end(people), s, comp);
}

演示。


编辑2:但是,鉴于这些或多或少丑陋的东西,您也可以遵循主要思想的路线,并在值周围使用一个小包装器作为键,例如

struct PersonKey
{
    PersonKey(std::shared_ptr<Person> const& p) : s(p->name) {}
    PersonKey(std::string const& _s) : s(_s) {}
    std::string s;
    bool operator<(PersonKey const& rhs) const
    {
         return s < rhs.s;
    }
};

像(未经测试(一样使用它

std::map<PersonKey, std::shared_ptr<Person> > m;
auto sptr = std::make_shared<Person>("Peter");
m[PersonKey(sptr)]=sptr;

查找是通过

m[PersonKey("Peter")];

现在我比我的第一个建议更喜欢这个;-(

这是大卫海尔答案的另一种选择。

struct Person {
    // lots of other values
    std::string name;
}
struct StrPtrCmp {
    bool operator()(const std::string* a, const std::string* b) const {
        return *a < *b;
    }
}
std::map<const std::string*, std::shared_ptr<Person>, StrPtrCmp> people();
void insertPerson(std::shared_ptr<Person>& p) {
    people[&(p.name)] = p;
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
    return people[&name];
}

并进行一些编辑以使其与std::unordered_map一起使用:

struct StrPtrHash {
    size_t operator()(const std::string* p) const {
        return std::hash<std::string>()(*p);
    }
};
struct StrPtrEquality {
    bool operator()(const std::string* a, const std::string* b) const {
        return std::equal_to<std::string>()(*a, *b);
    }
};
std::unordered_map<const std::string*, std::shared_ptr<Person>, StrPtrHash, StrPtrEquality> people();