3

I have two classes, A and B, which both need to be able to reference each other, as shown below.

// A.h
class B; // Forward declare B

template<>
struct std::hash<B>; // Forward declare hash template specialization for B

class A {
    std::unordered_map<B, int> map;
};
// B.h
#include "A.h"

class B {
    A object;
}

template<>
struct std::hash<B> {
    size_t operator()(__in const B& toHash) const;
}

In this current state, it tells me error C2139: 'std::hash<B>': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_empty'

If I don't forward declare the hash template specialization, it tells me that I can't declare it after it's already used.

I also tried moving the hash template specialization's definition to where I'm forward declaring it now, and that also gave me an error.

What can I do to cause this to compile and behave the way I've laid out here?

The only thing I can think of would be to redefine B to instead store something like a std::unique_ptr<A>, forward declare A instead of B, and have A import B.h rather than B import A.h. But this feels like a weird solution that doesn't really address the problem of dependencies like this. Am I missing something?

James McDowell
  • 2,668
  • 1
  • 14
  • 27
  • It's a bit a moot question. `B` being incomplete isn't allowed ([SO post](https://stackoverflow.com/questions/13089388/how-to-have-an-unordered-map-where-the-value-type-is-the-class-its-in)) in a container, and doesn't work in any implementation I tried. – StoryTeller - Unslander Monica Jun 26 '20 at 00:15
  • That is part of the question here. As far as I can tell, the hash specifically is the reason why, but I could be wrong about that. How can I best fix this cyclic-appearing dependency? – James McDowell Jun 26 '20 at 01:04
  • The hash isn't why. The error I continuously see is in the unordered_map itself, when it tries to instantiate its node type. – StoryTeller - Unslander Monica Jun 26 '20 at 01:06
  • 3
    Another option is to put the `unordered_map` behind a pointer, as [I suggest a couple iterations of this question back.](https://stackoverflow.com/questions/61356085/why-does-the-following-code-compile-using-clang-but-not-gcc/61357337#61357337) And no, `std::hash` is not the issue. The C++ standard says that `B` must be complete for `std::unordered_map` to be complete, and you need that for `A` to be definable, but you need `A` already defined and complete to make `B` complete: a cycle. There's no conformant way to make this work as is, period. Put pointers *somewhere*. – HTNW Jun 26 '20 at 01:06
  • I don't see anything that actually prevents the specialization of `std::hash`, together with its `()` operator, to be defined before the definition of `A`, and then implement the `()` operator somewhere else. – Sam Varshavchik Jun 26 '20 at 01:06

1 Answers1

0

For forward declaration of class B, the compiler has the name of class B, but no nothing about its attributes (size).

So that you can't use it, to defined std::unordered_map<B, int> map;, but you can make undefined std::unordered_map, using pointers (*) or reference (&)

static std::unordered_map<B, int> &map;

or

std::unordered_map<B, int> *map;
4.Pi.n
  • 1,151
  • 6
  • 15