使用unordered_map,其中Key是T的成员
Using an unordered_map where Key is a member of T
是否有任何好的方法来使用unordered_map,以便您可以在常量时间(平均情况下)通过成员变量访问对象?下面的示例具有此功能,但需要将每个Person
的名称复制为Key:
#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
模板类,可以做到这一点整齐吗?
如果您不反对使用Boost,那么就使用Boost。MultiIndex使得这非常简单,而不会增加任何不必要的低效率。下面是一个有效地创建Person
对象的unordered_set
的示例,它以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
-reference返回,而不是按值返回,但这个更改并不是严格要求的。)
应该注意的是,c++ 14对透明比较器的支持将允许您在这里使用std::unordered_set<Person>
而不需要Boost。
显而易见的解决方案是复制关键部分,并使用std::unordered_map
;对于小型的、局部的使用,这是最简单和最有效的方法这是首选的解决方案,但是没有强制的不变量key == mapped.key_part
,这使得代码更难维护插入可能发生在代码中的多个不同位置。
如果您在映射中保留指针,而不是值,那么可以对映射进行包装,以便将键安排为指向适当的字段。当然,问题在于,这意味着保留指针,而不是值。
如果在映射中不修改值,则可以直接使用std::set
,而不是std::map
,使用适当的散列和平等的功能。如果你这样做,你可能需要构造一个虚拟Person
对象,以便访问数据。
如果要修改值(当然不包括键),则您的问题是std::set
只提供const
访问其成员。你可以使用const_cast
来解决这个问题,但它很难看。(而且容易出错;你如何确保真正的关键部件不是修改。)
这些解决方案都不完全令人满意。像你这样的案子没有对关键部分(名称)的可变访问,我可能会使用前两种解决方案之一,将unordered_map
包裹在我自己的一个类,用来确保不变量被维护。
您正在寻找的是unordered_set
,而不是地图。set使用该值作为键和值。
不要改变value的"key"部分
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 助记符和指向成员语法的指针
- 用于访问容器<T>数据成员的正确 API
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 嵌套在类中时无法设置成员数据
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 将函数类成员映射到类本身内部
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 将公共但非静态的成员函数与ALGLIB集成
- 使用unordered_map,其中Key是T的成员
- 以指向成员的指针作为比较器/"key"的 STD 算法