Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointers as keys in map C++ STL

I have a question on how pointers to a custom object are handled when used as Keys in an map. More specifically if I define

std::map< CustomClass*, int > foo;

Would the default C++ implementation work to handle these pointers? Or do I need to define a custom comparator function to handle it? In general, is it good practice to use pointers to objects as keys?

like image 758
Ammar Husain Avatar asked Aug 04 '14 16:08

Ammar Husain


People also ask

Can you use a pointer as a key in a map?

C++ standard provided specialisation of std::less for pointers, so yes you can safely use them as map keys etc.

How can I use multiple values with one key in a C++ STL map?

The is many ways to store multiple values with one key. The simplest way is to use multimap and unordered_multimap. You could also us an key with a container (like vector, deque, set, or unordered_set) of value types. std::pair if you need two values per key, std::tuple if you need more than two values per key.

Can you hash a pointer C++?

Is it safe to use? Is this good practice? you probably mean std::unordered_map and the answer is practicaly the same: you can use pointers if you really mean to hash pointers (and not the object they point to). You can as well implement your own hash (or hash-redirection) to work with the pointed-to objects.


Video Answer


5 Answers

The default implementation will compare the addresses stored by the pointers, so different objects will be considered as different keys. However, the logical state of the object will not be considered. For example, if you use std::string * as the key, two different std::string objects with the same text of "Hello" would be considered a different key! (When stored in the map by their addresses)

It's ok to use pointers as keys so long as you understand the important difference above.

like image 134
Neil Kirk Avatar answered Sep 30 '22 20:09

Neil Kirk


Pointers will be handled but compared as pointers (memory order). You have to pass custom less functor if you wish to compare the objects:

template<class T> struct ptr_less {
    bool operator()(T* lhs, T* rhs) {
        return *lhs < *rhs; }};
map<Object*,int,ptr_less<Object>> mymap;
like image 41
firda Avatar answered Sep 29 '22 20:09

firda


C++ standard provided specialisation of std::less for pointers, so yes you can safely use them as map keys etc.

like image 39
Wojtek Surowka Avatar answered Sep 28 '22 20:09

Wojtek Surowka


Leaving aside from the legality of this and any possible semantic misunderstandings, addressed already, I cannot think of any reason to use std::map here rather than std::unordered_map. There are early intercepts of this in Boost and Visual C++ if you are on a pre-C++11 compiler.

Since you appear to be using a pointer to represent a unique object, something like boost::flyweight could be applicable.

like image 26
Steve Townsend Avatar answered Sep 30 '22 20:09

Steve Townsend


Pointers can be used as keys but especially with a std::map (or std::set) I would not advise it. The behavior of the program is not deterministic i.e. when one iterates over the map the order in which the items in the map are iterated is not guaranteed to be the same. It really depends on the memory address of the object (key). Take a look at this example, as you can see irrespective of the insertion order into the map the items are iterated in a deterministic way when the key is a string rather than a pointer.

http://ideone.com/VKirct

#include <iostream>
#include <map>
using namespace std;

class SomeClass {
    public:
    SomeClass(const std::string& name): m_name(name) {}
    std::string GetName()const {return m_name; }
    bool operator <(const SomeClass& rhs) const { return m_name < rhs.m_name; }
    private:
    std::string m_name;
};

auto print_seq  = [](const auto& seq) { for (const auto& itr: seq) {std::cout << itr.second << " , ";} std::cout << std::endl;};

int main() {
    // your code goes here
    std::map<SomeClass*, std::string> pointer_keyed_map;
    SomeClass s3("object3");
    SomeClass s1("object1");
    SomeClass s2("object2");
    pointer_keyed_map.insert(std::make_pair(&s1, s1.GetName()));
    pointer_keyed_map.insert(std::make_pair(&s2, s2.GetName()));
    pointer_keyed_map.insert(std::make_pair(&s3, s3.GetName()));
    std::cout << "Pointer based keys: object order" << std::endl;
    print_seq(pointer_keyed_map);

    std::map<SomeClass, std::string> int_keyed_map;
    int_keyed_map.insert(std::make_pair(s3, s3.GetName()));
    int_keyed_map.insert(std::make_pair(s1, s1.GetName()));
    int_keyed_map.insert(std::make_pair(s2, s2.GetName()));
    std::cout << "String based keys: object order" << std::endl;
    print_seq(int_keyed_map);
    return 0;
}
like image 37
Chenna V Avatar answered Oct 01 '22 20:10

Chenna V