std::unordered_set<Foo> as member of class Foo
Asked Answered
B

2

5

I'm writing a class that has an unordered_set of its own type as a member. Therefore I need to write a specialization for hash<Foo>. This specialization needs to be defined after Foo is declared. But it seems to me as if I already need the specialization for hash<Foo> before defining the member unordered_set<Foo>. At least it doesn't compile and fails there. I tried a forward declaration of the hash template but couldn't get it working thereby either.

The relevant code snippet is:

class Foo {
public:
    int i;
    std::unordered_set<Foo> dummy;
    Peer(std::unordered_set<Foo>);
};

namespace std {
    template<> struct hash<Foo>
    {
        size_t operator()(const Foo& f) const
        {
            return hash<int>()(f.i);
        }
    };
}

Thanks in advance

Barrack answered 8/1, 2012 at 22:22 Comment(0)
M
8

Foo cannot have a member variable of type std::unordered_set<Foo>.

You cannot instantiate a Standard Library container with an incomplete type. Basically, with several exceptions not relevant here, a class type is not complete until the } that terminates its definition.

You'll either need to store some other type in the container (perhaps std::unique_ptr<Foo>), or use a containers library that provides containers instantiable with an incomplete type (e.g., Boost has such a containers library).

Mammet answered 8/1, 2012 at 22:24 Comment(2)
Thanks for the fast answer, then I'll try it differentlyBarrack
For some reason, Clang will allow this behavior. GCC correctly identifies a problem.Woodwaxen
B
1

You can move the declaration around a bit to make it compile:

class Foo;

namespace std {
  template<> struct hash<Foo> {
    size_t operator()(const Foo& f) const;
  };
}

class Foo {
public:
  int i;
  std::unordered_set<Foo> dummy;
  Foo(std::unordered_set<Foo>);
};

namespace std {
  size_t hash<Foo>::operator()(const Foo& f) const {
    return hash<int>()(f.i);
  }
}

As James says, though, the declaration of dummy is is undefined behaviour.

You will also need an equality comparison; easiest to add an operator== to Foo.

I would also recommend making the constructor of Foo take the argument by const-reference.

Behistun answered 8/1, 2012 at 22:26 Comment(2)
Yes, it compiles. But as it actually shouldn't compile because it's not standart compilant I should not use it like this, right?Barrack
@devmapal: Absolutely right, don't use this. I just posted in in case you have other template scenarios in the future where this might apply. Note that the problem is only that the standard says you mustn't to this with standard library templates. You're free to do this with your own templates if you can prove that it does the right thing.Behistun

© 2022 - 2024 — McMap. All rights reserved.