有没有什么好方法可以使用unordered_map,以便您可以在常量时间(平均大小写)中通过成员变量访问对象?以下示例具有此功能,但要求将每个Person
的名称复制为密钥:
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm>
class Person {
public:
Person() : name_("") {}
Person(const std::string& name) : name_(name) {}
std::string getName() const { return name_; }
void kill() const { std::cout << name_ << " is dead!" << std::endl; }
private:
std::string name_;
};
int main(int argc, const char* argv[]) {
Person p1("dave");
Person p2("bob");
std::unordered_map<std::string, Person> map = {
{p1.getName(), p1}, // Duplicating the
{p2.getName(), p2} // keys here
};
map["dave"].kill();
return 0;
}
我认为value_type
本身需要Person
本身,而不是pair<string, Person>
而unordered_map
需要知道使用Person::getName
当散列和访问对象时。
理想的解决方案允许我设置unordered_map
(或unordered_set
,如果它更适合作业)知道使用Person::getName
来获取每个对象的密钥。然后,我可以简单地通过给出对象来插入它们(并且没有键,因为它知道如何获取密钥)并通过给出比较等于Person::getName
的返回值的键来访问它们。
有些事情:
// Pseudocode
std::unordered_map<Person, Person::getName> map = {p1, p2};
map["dave"].kill();
那么可以实例化一个可以做到这一点的unordered_map
模板类吗?
答案 0 :(得分:3)
如果您不反对使用Boost,那么Boost.MultiIndex会使这非常简单,而不会增加任何不必要的低效率。以下是一个有效创建unordered_set
Person
个Person::getName()
个对象的示例,该对象以#include <string>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
namespace bmi = boost::multi_index;
struct Person
{
Person() = default;
Person(std::string const& name) : name_(name) { }
std::string const& getName() const noexcept { return name_; }
void kill() const { std::cout << name_ << " is dead!\n"; }
private:
std::string name_;
};
typedef bmi::multi_index_container<
Person,
bmi::indexed_by<
bmi::hashed_unique<
bmi::const_mem_fun<Person, std::string const&, &Person::getName>
>
>
> PersonUnorderedSet;
int main()
{
Person p1("dave");
Person p2("bob");
PersonUnorderedSet set;
set.insert(p1);
set.insert(p2);
set.find("dave")->kill();
// for exposition, find is actually returning an iterator
PersonUnorderedSet::const_iterator iter = set.find("bob");
if (iter != set.end())
iter->kill();
// set semantics -- newly_added is false here, because
// the container already contains a Person named 'dave'
bool const newly_added = set.insert(Person("dave")).second;
}
的值为基础:
Person::getName()
(请注意,为了提高效率,我将const
的签名更改为std::unordered_set<Person>
- 引用而不是值,但不是严格要求更改。)
应该注意的是,C ++ 14对transparent comparators的支持将允许您在此处使用{{1}}而无需任何Boost。
答案 1 :(得分:2)
显而易见的解决方案是只复制关键部分,然后使用
std::unordered_map
;对于小型的本地化使用,这是最简单的
首选解决方案,但没有强制执行不变量
key == mapped.key_part
,这使代码难以维护
插入可能出现在代码中的几个不同位置。
如果你在地图中保留指针而不是值,那么你可以 包装地图以安排键成为指向适当的指针 值中的字段。当然,问题在于它意味着什么 保持指针,而不是价值观。
如果您在地图中修改了值,则可以使用
std::set
,而不是std::map
,具有适当的哈希值
平等功能。如果你这样做,你可能需要构建一个
虚拟Person
对象,以便访问数据。
如果要修改值(当然不包括键),那么
您遇到std::set
仅提供const
访问权限的问题
成员。你可以使用const_cast
解决这个问题,但这很难看。
(并且容易出错;如何确保实际的关键部分不是
修改。)
这些解决方案都不是完全令人满意的。在像你这样的情况下,
我可能没有对关键部分(名称)的可变访问权限
使用前两个解决方案之一,将unordered_map
包装进去
我自己的一类,以确保维持不变量。
答案 2 :(得分:1)
您要找的是unordered_set
,而不是地图。该集合将值用作键和值。
只是不要更改值的“关键”部分。