`std :: unordered_map`没有复制关键数据

c z*_*c z 7 c++ unordered-map stdmap c++11

我有一个Person类,它有一个nameproperty(std::string).

我想创建一个查找表,一个std::unordered_map,所以我可以找到一个Person名字.但是,鉴于a Person,我也希望能够得到他们的名字.

这需要存储name两次 - 一次作为地图的键,一次存储在人物对象内,如下面的代码所示.

由于我有很多Persons一次加载到内存中,我不希望两次存储它们的名称的开销.

我已经尝试在Person类中使用键的引用/指针,但这会产生问题,因为映射似乎在修改时重新调整其数据,并且引用变为无效.

我也尝试过使用std::unordered_set,但这意味着我Person每次想要执行查找时都需要构造一个完整的对象.

是否有任何方法可以使无序映射的键和值共享相同的数据?

#include <iostream>
#include <unordered_map>


class Person
{
    private:
        const std::string _name;

    public:
        Person( const std::string& name ) : _name( name )
        {
        }


        const std::string& get_name() const
        {
            return _name;
        }
};


int main()
{
    auto my_set = std::unordered_map<std::string, std::shared_ptr<Person>>();

    my_set.insert( { "alice", std::shared_ptr<Person>( new Person( "alice" )) } );
    my_set.insert( { "bob", std::shared_ptr<Person>( new Person( "bob" )) } );
    my_set.insert( { "charlie", std::shared_ptr<Person>( new Person( "charlie" )) } );

    std::cout << my_set.find( "bob" )->second->get_name() << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Jar*_*d42 2

对于std::set,您可以使用透明比较器(std::unordered_set似乎不支持:/):

struct LessPerson
{
    using is_transparent = void; // enable "transparent" comparer

    template <typename T1, typename T2>
    bool operator ()(const T1& t1, const T2& t2) const
    {
        // Compare only "name".
        return toString(t1) < toString(t2);
    }

    // trivial one
    const std::string& toString(const std::string& s) const
    {
        return s;
    }

    // the one why we create the class
    const std::string& toString(const Person& p) const
    {
        return p.get_name();
    }

    // A tricky one to handle dereference of (smart) pointers.
    template <typename T,
              std::enable_if_t<std::is_same<Person, std::decay_t<decltype(*std::declval<T>())>>::value>* = nullptr>
    const std::string& toString(const T& p) const
    {
        return (*p).get_name();
    }

};
Run Code Online (Sandbox Code Playgroud)

然后使用它:

auto my_set = std::set<std::shared_ptr<Person>, LessPerson>();

my_set.insert( { std::make_shared<Person>("alice") } );
my_set.insert( { std::make_shared<Person>("bob") } );
my_set.insert( { std::make_shared<Person>("charlie") } );

auto it = my_set.find("bob"); // search using "bob" directly without creating a new Person
Run Code Online (Sandbox Code Playgroud)

演示