I had a problem with include order that I cannot explain. I will show you a minimal example with four files:
// A.h
#pragma once
#include <functional>
struct A {};
namespace std {
template<>
class hash<A> {
public:
size_t operator()(const A&) const {
return 0;
};
};
}
// B.h
#pragma once
#include <unordered_map>
struct A;
struct B {
const std::unordered_map<A, int>& GetMap() const;
};
// B.cpp
#include "B.h"
#include "A.h"
const std::unordered_map<A, int>& B::GetMap() const {
static std::unordered_map<A, int> m;
return m;
}
// main.cpp
#include "A.h" // To be included AFTER B.h
#include "B.h"
int main() {
B b{};
const auto& m = b.GetMap();
}
With this example I get the following error:
error LNK2019: unresolved external symbol "public: class std::unordered_map<struct A,int,class std::hash<struct A>,struct std::equal_to<struct A>,class std::allocator<struct std::pair<struct A const ,int> > > const & __cdecl B::GetMap(void)const " (?GetMap@B@@QEBAAEBV?$unordered_map@UA@@HV?$hash@UA@@@std@@U?$equal_to@UA@@@3@V?$allocator@U?$pair@$$CBUA@@H@std@@@3@@std@@XZ) referenced in function main
1>Z:\Shared\sources\Playground\x64\Debug\Playground.exe : fatal error LNK1120: 1 unresolved externals
But if in main.cpp
I include A.h
after B.h
, the program compiles successfully.
Can someone explain why?
It took me a long time to find the problem in real code, there is some method to make me understand easily that the error is related to include order?
Edit: I made some other test to investigate the problem.
The error occurs also if I change the std::unordered_map<A, int>
with std::unordered_set<A>
but not with std::map<A, int>
and std::set<A>
, so I think that there is some problem with the hash.
As suggested, including A.h
in B.h
instead of forward declaring A makes the build succeed without modifying the include order in main.cpp
.
So I think that the question become: why forward declaring A, and thus having an incomplete type for the key of unordered map, causes the error?