Why can't I replace std::map with std::unordered_map
I guess that because std::unordered_map needs to rehash, and therefore copy elements, the types need to be complete, whereas a map, only ever working with pointers to elements, will not exhibit that problem.
The solution here is to have an unordered map to a pointer:
std::unordered_map<uint32_t, std::shared_ptr<Test> >.
In practice, when `std::unordered_map` must be used instead of `std::map`?
I know the difference between them, say internal implementation,time complexity for searching element
In that case, you should know that that the average asymptotic element lookup time complexity of unordered map is constant, while the complexity of ordered map is logarithmic. This means that there is some size of container at which point the lookup will be faster when using unordered map.
But I really can't find a circumstance where std::unordered_map could not be replaced by std::map indeed.
If the container is large enough, and if you cannot afford the cost of ordered map lookup, then you cannot choose to replace the faster unordered map lookup.
Another case where ordered map cannot be used is where there doesn't exist a cheap function to compare relative order of the key.
std::unordered_map insert invalidates only iterators but not references and pointers to the element node
Insertion of unordered_map
doesn't invalidate references because it doesn't move the data, however the underlying data structure might change rather significantly. Details of how exactly it is implemented aren't specified and different compilers do it differently. For instance, MSVC has a linked list for data storage and, I believe, a vector for the look-up. And insert
can cause rehash, meaning look-up gets changed completely and the linked list gets reorded significantly - but original data is not moved.
The iterators refer to this underlying structure and any changes to it can cause iterators to invalidate. Basically, they contain more info than pointers and references and subsequently can get invalidated.
The confusing passage is about insert
of a node_type
- nodes that were previously extracted. Checkout the method unordered_map::extract
https://en.cppreference.com/w/cpp/container/unordered_map/extract
For some reason it is forbidden to use pointers/references while the node is extracted. After inserting it is allowed to use them again. I don't know why it is so.
Why this include order causes link error on unordered_map?
I tested the same code in Visual Studio 2022 and got the same error. After my exploration, I found the problem.
Firstly, I copied the contents of A.h and B.h into main.cpp and removed the #include
directive. After compiling, I still got the same error.
Then I tested and found that as soon as I moved namespace std {...}
after the definition of class B
, the error went away.
I read the assembly code generated by the compiler and found that the names generated for GetMap
in main.cpp and b.cpp are different:
main.asm:
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
b.asm:
GetMap@B@@QEBAAEBV?$unordered_map@UA@@HU?$hash@UA@@@std@@U?$equal_to@UA@@@3@V?$allocator@U?$pair@$$CBUA@@H@std@@@3@@std@@XZ
I looked up MSVC's name mangling rules and found U
for struct
and V
for class
. So I change the definition of template<> class hash<A>
to template<> struct hash<A>
. Then the error disappeared.
I think it's legal to use class
keyword instead in the specialization, but I can't find a description of this in the standard.
However, I think the problem may not be that simple. A key issue here is that the specialization of std::hash
in B.cpp appears after the definition of class B
, while in main.cpp the order is completely reversed. I think this violates the ODR and should result in undefined behavior. This is also why the program is correct after swapping the order of the header files (making it consistent with the order in B.cpp).
I looked up some sources and couldn't find a standard description of the problem: In B.cpp, the declaration of GetMap
does not cause unordered_map
to be instantiated, but it is instantiated in the function definition. A specialization of std::hash
was inserted in the middle of declaration and definition, which could cause unordered_map
to see a different definition of std::hash
. Can the compiler see this specialization? Should the compiler choose this specialization? If the compiler can see the specialization, why is the primary template used in the generated assembly code? (The compiler-generated name in B.cpp uses "U", which is for struct
)
Modify the value of a std::unordered_map element in C++
If the key isn't in the map, operator []
is required to create one. The expression
points[1]
needs to be able to default-insert a Point
in case of lookup failure (regardless of whether lookup failure ever occurs - this is a compile-time requirement not a run-time check). That requirement cannot be satisfied by Point
because Point
is not default constructible. Hence the compile error. If you want to use unordered_map::operator[]
, you'll need to add a default constructor.
If a default constructed Point
doesn't make sense for your usage - then you simply cannot use operator[]
and will have to use find
throughout (or at()
if you're okay with exceptions):
auto it = points.find(1);
if (it != points.end()) {
it->second.x = 20.f;
}
points.at(1).x = 20.f; // can throw
Using std::map or std::unordered_map when storing consecutive indices
Choosing between std::map
and std::unordered_map
comes down to if you care about ordering or lookup.
If you need ordering, then you want std::map
, at the cost of slower lookup (logarithmic).
If you need fast lookup, they you want std::unordered_map
, at the cost of no guaranteed ordering when looping.
However, in your case, since the elements have consecutive keys, why not just store the old file paths in a std::vector<std::string>
where the elements align with your std::vector<CustomData>
? Here you have the same ordering and the lookup is also constant time (and likely better then a hash map, since youre just jumping to an index rather than hashing the key).
Compilation error related to map and unordered_map: attempting to reference a deleted function
Following on from @JohnZwinck's (excellent) answer, I would say that using std::unordered_map
with a vector
as a key is usually a bad idea, because of the likely high cost of implementing any kind of effective hashing function.
The link John gave expands on this, but, essentially, the hash function has to inspect every element in the vector every time anything needs to be hashed. If the vector is large, well, ouch!
So std::map
is likely to be a better choice here, because std::less
(-> operator<
) is likely to be cheap - as soon as we hit an element of the vector whose value differs between the two operands, we are done. Worst case, it is no more expensive (although it's true that std::unordered_map
is more efficient than std::map
when a cheap and effective hashing function does exist, especially, for example, if the key is something like an int
).
Can you turn std::map into an unordered map with a custom comparator?
Always returning true is invalid. It would mean (for instance) that A < B
and B < A
would both be true. This contradicts the requirements of a std::map
comparator, which are that it imposes a strict weak ordering. It's entirely possible returning true would crash your program.
Always returning false is valid, it effectively means that all keys are considered equal. So only one key could be added to the map (thanks aschepler for the correction).
What stops you writing a sensible comparator?
Related Topics
How to Get a Non-Const C String Back from a C++ String
Opengl: Glflush() VS. Glfinish()
Class Members and Explicit Stack/Heap Allocation
Difference Between Std::Function<> and a Standard Function Pointer
Svm Classifier Based on Hog Features for "Object Detection" in Opencv
Creating JSON Arrays in Boost Using Property Trees
What Is the Purpose of the "Final" Keyword in C++11 for Functions
What Does -Fpic Mean When Building a Shared Library
How Does a Sentinel Node Offer Benefits Over Null
What Does an Object Look Like in Memory
What Is a Good Naming Convention for Vars, Methods, etc in C++
Initialize a Vector to Zeros C++/C++11
What Is the Use of "Using Namespace Std"