dimanche 10 octobre 2021

How to correctly alias a base class member in C++?

I wrote an Edge class like this:

struct Edge : public ::std::pair<int, int>
{
  using ::std::pair<int, int>::pair;
  int &src = first;
  int &dst = second;
};

namespace std
{
  template <>
  struct hash<Edge>
  {
    std::size_t operator()(Edge const &x) const noexcept
    {
      return (x.src << 16) | x.dst;
    }
  };
}

So I can use src/dst to substitute first/second.

However, I found things go wrong when I use std::unordered_set.

Edge e(1, 2);
print_edge_debug_info(e);
using _set_t = std::unordered_set<Edge>;
_set_t s;
s.insert(e);
for(auto &&_e : s) {
  print_edge_debug_info(_e);
}
/*
output:
 (1, 2) [ 2,0x7ffca1c08724]  [ 2,0x7ffca1c08724] 
 (1, 2) [ 2,0x2bb80fc]  [ 2,0x7ffca1c08724]
*/

print_edge_debug_info:

inline void print_edge_debug_info(Edge const &edge)
{
  std::cout << edge
            << " "
            << "[ " << edge.second << "," << &edge.second << "] "
            << " "
            << "[ " << edge.dst << "," << &edge.dst << "] "
            << std::endl;
}

The dst and second have the same address after the first Edge object being constructed. But If I put this Edge object into a std::unordered_set and fetch it from the set, the addresses of the result's dst and second are different.

Besides, the new second and the old second have different address. But the new dst and the old dst have the same address. Which means the new dst is the alias of the old second, not the alias of the new second.

It seems strange for me. I don't understand why this happened.

Is my way to make member aliases wrong? What's the correct way?

Aucun commentaire:

Enregistrer un commentaire