Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using templates as keys in a std::map

I am wondering whether it is possible to use a template as a key for a map. For instance:

std::map< <T> , Node> nodes;

In essence, what I want to do is be able to have a bunch of nodes that contain arbitrary types data, and are keyed by that data. I think I could do it by converting said data to binary and keying by that, but that is messy and I want to avoid it.

To clarify, I want to be able to use a variable of any type as the key. For example if I have 2 nodes, one that contains an int as its data, and another that contains a Foo as its data, I want to be able to put those in the same map using their data as the key. Maybe a map is not what I want, I am not sure...

Thoughts? Thanks!

like image 777
vtleavs Avatar asked Jun 21 '16 15:06

vtleavs


3 Answers

If you didn't disabled RTTI explicitly, refer to nogard's answer. Standard type id is garanteed to be unique for a whole program across DLL. This is not true for the address of a function.

What I usually do is this:

template<typename T>
void type_id(){}

using type_id_t = void(*)();

Then, I use it like this:

std::map<type_id_t, Node> nodes;

nodes[type_id<AType>] = Node{...};
nodes[type_id<BType>] = Node{...};

Of course, this can be enhanced with variable template from C++14.


Sorry, I just re-read the question and I understand it better.

What you want is std::any, but it's C++17 only. You can use boost::any instead.

It will look like this:

std::map<std::any, Node> nodes;

nodes.emplace("string as key", Node{});
nodes.emplace(23, Node{});

It should work as long as the map can somehow order instances of std::any. If not, you can use hash map instead:

std::unordered_map<std::any, Node> nodes;

Then it will work as long as the map can hash an any.

like image 104
Guillaume Racicot Avatar answered Sep 28 '22 09:09

Guillaume Racicot


I think for this purpose it's much easier to use std::type_info for the types:

std::map<std::type_info, std::string> m;
m[typeid(int)] = "integer";

But this really depends on what you want to achieve, it's still unclear to me. Hope that helps

like image 23
nogard Avatar answered Sep 28 '22 07:09

nogard


At least if I understand what you want, the short answer is no.

The keys in a map must be ordered--that is, you must define for any pair of keys A and B, you must define an ordered relationship where either A is less than B, or B is less than A, or the two keys are equivalent.

Given two keys of entirely arbitrary types there's not going to be a defined way of comparing them. Therefore, you can't use them as keys in for a map.

To get something sort of close, you'll need to define some specific set of types you want to support. Then you can define something that's (roughly) a union of all the types you want to support. That alone won't be enough though--you'll also have to define ordering. Depending on what you're trying to accomplish, you might have (for example) an ID in every one of those objects, and order them by ID. Alternatively, you might define an ordering between objects, so (for example) every Person sorts before every Dog, which sorts before every Dream, and so on. Then you'll have to define an ordering within each type about like you usually would.

I'd warn, however, that this typically involves quite a bit of extra work, and provides very little value in return. I'd say well over 90% of the time I've seen people (try to) do this, it's been a mistake that worked out poorly at best. If at all possible, I'd try to find some other approach to whatever problem you're trying to solve.

like image 42
Jerry Coffin Avatar answered Sep 28 '22 07:09

Jerry Coffin