I have a project where I’m putting objects in an unordered_map that can logically chain together with other objects in the map. To represent this chain, I store a reference to the next object in the chain’s map key, so I end up with something resembling a linked list within the map:
class Foo
{
int *next;
// other fields, methods, etc.
};
std::unordered_map<int, Foo> foos;
I’ve added a helper method that follows the linked list for an object and returns pointers to the following objects in a vector:
class Foo
{
int *next;
std::vector<Foo *> following(std::unordered_map<int, Foo> &foos) const
{
std::vector<Foo *> result;
const Foo *current = this;
while (current->next)
{
auto nextIt = foos.find(*current->next);
if (nextIt == foos.end()) break;
result.push_back(&nextIt->second);
current = &nextIt->second;
}
return result;
}
I also want to have a version of this helper that works on a const reference to the map and returns const pointers, i.e. std::vector<const Foo *> following(const std::unordered_map<int, Foo> &foos) const. I can of course create this just by duplicating the code, but is there a way to achieve this without duplication through templating or something similar?
>Solution :
Yes you can do this with a little template hackery.
template <typename K>
auto following(K &foos) const
{
using elem = std::remove_reference_t<decltype((foos.begin()->second))>;
std::vector<elem *> result;
const Foo *current = this;
while (current->next)
{
auto nextIt = foos.find(*current->next);
if (nextIt == foos.end()) break;
result.push_back(&nextIt->second);
current = &nextIt->second;
}
return result;
}
If you use C++20 or newer, you can restrict the template parameter to get better error messages:
template <typename K>
requires std::is_same_v<const K, const std::unordered_map<int, Foo>>