I'm trying to keep information in std::map. But I have a problem with find:
typedef map<string, string, equal_to<string>> MAP_STRING2STRING;
....
MAP_STRING2STRING map;
MAP_STRING2STRING::const_iterator iter;
When I try to find key, I get the following error:
iter = map.find(key);
What am I doing wrong?
This error appears only when I have something in map.
Your map has the wrong kind of comparison functor. You need strict weak ordering (less-than or greater-than type comparison), not equality. You can just omit the comparison functor parameter and use a less-than comparison for std::string. This implements strict weak ordering via a lexicographical comparison of strings:
typedef map<string, string> MAP_STRING2STRING;
This is equivalent to
typedef map<string, string, less<string> > MAP_STRING2STRING;
Internally, the map uses the strict weak ordering comparison to both order itself, and determine whether two keys are the equal.
The third template parameter allows you to instantiate a map with a custom ordering criterion. For example, this would create a map with the reverse ordering of the one above:
typedef map<string, string, greater<string> > MAP_STRING2STRING;
If you want a simple map from std::string to std::string, just use map<string, string> (drop that equal_to<string> bogus "comparator").
Moreover, since you have a variable named "map", this can cause conflict with the STL map class. Either change the variable name (e.g. call it myMap), or use the std:: namespace prefix for std::map class:
typedef map<string, string> MAP_STRING2STRING;
....
MAP_STRING2STRING myMap;
In addition, since from the error message you are using VS2010, you can use the convenient auto C++11 keyword, to avoid the "clutter" of MAP_STRING2STRING::const_iterator, and just use:
auto iter = myMap.find(someKey);
Related
It seems like when I try to define an unordered_set of vector, I get an error saying: "Call to implicitly-deleted default constructor of unordered_set< vector<int> > ." This doesn't happen when I define a regular (ordered) set: set< vector<int> >. It seems like I need to define hash<vector<int>> in order to get rid of the error.
Does anyone know why I get this error only when I use unordered_set? Shouldn't both data structures use hashing, so why would an unordered_set need a custom-defined hash function? In fact, shouldn't a regular (ordered) set need some custom-defined comparator as well in order to order the vector<int> data structures?
It's because unordered_set is using std::hash template to compute hash for its entries and there is no std::hash for pairs. You have to define custom hash to use unordered_set.
struct vector_hash
{
template <class T1, class T2>
std::size_t operator () (std::pair<T1, T2> const &v) const
{
return std::hash<T1>()(v.size());
}
};
and then declare your unordered_set as -
std::unordered_set< vector<int>, vector_hash> set;
This hash function is not good. It's just an example.
Shouldn't both data structures use hashing
No. This is documented, you can always look it up yourself:
std::set
std::set is an associative container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function Compare. Search, removal, and insertion operations have logarithmic complexity. Sets are usually implemented as red-black trees
Note that Compare defaults to std::less<Key>, and std::vector overloads operator<.
std::unordered_set, for comparison
Unordered set is an associative container that contains a set of unique objects of type Key. Search, insertion, and removal have average constant-time complexity.
Internally, the elements are not sorted in any particular order, but organized into buckets. Which bucket an element is placed into depends entirely on the hash of its value
and the Hash type parameter defaults to std::hash<Key>. This has a list of specializations for standard library types, and std::vector is not included on that list.
Unfortunately, there is no general specialization of std::hash for std::vector. There is only a specialization for std::vector<bool>, which is not very helpful:
int main()
{
std::hash<std::vector<bool>> hash_bool; // ok
std::hash<std::vector<int>> hash_int; // error
}
Demo.
The reason IMHO is that there is no standard way to combine hashes. Because you have to combine hashes for all elements in order to construct the hash for the whole vector. But why there is no standard way to combine hashes - this is the mystery.
You can use boost::hash or absl::Hash instead, e.g.:
unordered_set<vector<int>, boost::hash<vector<int>>>
Be aware that the hash codes computed by absl::Hash are not guaranteed to be stable across different runs of your program. Which might be either a benefit (if you aim for security), or a disadvantage (if you aim for reproducibility).
My answer is not related to std::vector issue directly, but in my case I've got the "Call to implicitly-deleted default constructor" error on std::string, just because I did not include the <string> header file.
I mean that that error can happen when unordered_map is used on forward declared type:
#include <unordered_map>
#include <string_view>
//#include <string>
int main()
{
auto m = std::unordered_map<std::string, int>{};
}
set use self balancing trees whereas unordered_set uses pure hashing.
In case of unordered_map we define the hash and pred functors whenever we are using user-defined keys.
The template syntax for a map is as follows:
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > // map::allocator_type
> class map;
In case of map there is no hash and pred functors option. Do we never have collisions in case of map. If collisions happen then why don't we have the hash and pred functors as in unordered_map?
Am I missing something here?
std::map and std::unordered_map are two different types of containers that both provided key-value pair mapping. How they do that though is completely different.
std::map uses a tree structure for its implementation. Typically this is an RBTree but any tree that can guarantee worst case O(logN) operations will work. This means it only needs to have a comparison operator for the key type since you can get total ordering and check for equality with a comparator that implements a strict weak ordering. This means you'll never have a hash collision since you aren't using a hash.
std::unordered_map is based on a hash table implementation. Since it hashes the key, you need a hash operator. You also need a comparison operator since two values could hash to the same value (hash collision). Without the comparison operator you would not be able to tell if the duplicate hash is really a duplicate item.
std::map is not a hash table and thus doesn't use a hash function. Instead, it requires operator< for ordering the values contained in the map.
For an std::map, can i always trust begin() to return the element with the smallest key according to comparison operators for the type, when iterating?
In other words...
Will std::map<Key, SomeClass>::iterator smallestKeyIt = someMap.begin(); give me the pair in the map with the smallest key?
Is this the ordering that is quaranteed for an std::map or can i configure it somehow? My understanding is that the underlaying tree structure is kept ordered when performing operations such as adding and removing elements.
can i always trust begin() to return the element with the smallest key according to comparison operators for the type?
Yes.
Is this the ordering that is quaranteed for an std::map or can i configure it somehow?
Yes. And you can configure the behaviour of comparing by specify the comparator. (The default one is std::less.)
From cppreference:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black trees.
std::map is defined as:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
You can use your specialized Compare to configure how the entries of a map are ordered. For example, if you use:
std::map<int, double, std::greater<int>> myMap;
then, the first entry in myMap will have the largest key.
What is the difference between the following two lines?
map<int, float> map_data;
map<const int, float> map_data;
int and const int are two distinct types.
std::map<int, float> and std::map<const int, float> are, similarly, different types.
The difference between std::map<const int, float> and std::map<int, float> is, to a degree, analogous to the difference between, say, std::map<int, float> and std::map<std::string, float>; you get a fresh map type for each.
In the non-const case, the internal key type is still non-const int:
std::map<const int, float>::key_type => const int
std::map<int, float>::key_type => int
However, map keys are semantically immutable, and all map operations that allow direct access to keys (for example, dereferencing iterators, which yields value_type) does constify the key_type:
std::map<const int, float>::value_type => std::pair<const int, float>
std::map<int, float>::value_type => std::pair<const int, float>
So the difference may be largely invisible to you in every way that matters, if your implementation allows it.
That's not always the case, though: the standard officially requires your key type to be copyable and moveable, and some implementations re-use map nodes; under those implementations, attempting to use a const key simply won't work.
The key is already const, so it is redundant to write const in this case. Once an element is entered, its key cannot be changed.
Edit:
As mentioned in the comments, there is difference between the two lines. For example, if you write a function that accepts map<const int, int>, you can't pass to it map<int, int> since they're different types.
But note that although they are different types, they behave the same since the key in a map is a const anyway...
So in conclusion.. The only difference is that they are two different types, you shouldn't care about anything else.
The difference is that the second variant will set the key type for the map as const int. From the "modifiability" point of view this is redundant, since the map already stores its keys as const objects.
However, this can also lead to unexpected and non-obvious differences in the behavior of these two maps. In C++ a template specialization written for type T is different from specialization written for type const T. That means the above two versions of the map might end up using different specializations of various "satellite" templates that depend on the key type. One example is the key comparator predicate. The first one will use std::less<int> while the second one will use std::less<const int>. By exploiting this difference you can easily make these maps to sort their elements in different order.
Issues like that are more obvious with the new C++11 containers like std::unordered_map. std::unordered_map<const int, int> will not even compile, since it will attempt to use a std::hash<const int> specialization for hashing the keys. Such specialization does not exist in the standard library.
const can't be altered once set. And yes as per docs & other answer you should remember that key is const already.
Link: http://www.cplusplus.com/reference/map/map/
Link: http://en.cppreference.com/w/cpp/container/map
While the behaviour of your application will typically be the same, it makes a difference to some compilers you might use. The more specific example of what brought me to this page in the first place:
Explicitly specifying a map as map<const key, value> builds successfully with the gnu toolkit;
However it crashes a Studio12 Solaris x86 build.
map<key, value> builds successfully on both. Behaviour of the application is unchanged.
Const keys can be helpful if the keys are pointers. Using const keys won't let you modify the pointed object when accessing the keys, consider this:
#include <map>
#include <string>
int glob = 10;
int main() {
std::map<const int*, std::string> constKeyMap { { &glob, "foo"} };
std::map<int*, std::string> keyMap { { &glob, "bar" } };
for(const auto& kv : keyMap) { *(kv.first) = 20; }; // glob = 20
for(const auto& kv : constKeyMap) { *(kv.first) = 20; }; // COMPILE ERROR
return 0;
}
const refers to a constant, that, once defined, can't be altered then... non const key is subjected to change... or cant even change, it's just that "no change" is guaranteed in const (once defined), and "change" may or may not occur in non const stuff.
C++, using Visual Studio 2010. A question about why a user-defined trait of hash_map actually requires total ordering.
I have a simple structure, say FOO, which only has a number of integers. I'd like to use hash_map, which is a hash table whose keys are unordered, to store the structure of FOO. I just need a fast searching of its associated value, so this is a right choice: hash_map<FOO, int32_t>.
However, I need to implement my own hash function and some compare functions for FOO. Here is the definitions of hash_map, taken from MSDN:
template <
class Key,
class Type,
class Traits=hash_compare<Key, less<Key> >,
class Allocator=allocator<pair <const Key, Type> >
>
class hash_map
It turned out that I needed to implement hash_compare functors:
template<class Key, class Traits = less<Key> >
class hash_compare
{
Traits comp;
public:
const size_t bucket_size = 4;
const size_t min_buckets = 8;
hash_compare( );
hash_compare( Traits pred );
size_t operator( )( const Key& _Key ) const; // This is a hash function
bool operator( )( // This is an ordering function
const Key& _Key1,
const Key& _Key2
) const;
};
Here is the detailed description of the bool operatod() from MSDN:
For any value _Key1 of type Key that precedes _Key2 in the sequence and has the same hash value (value returned by the hash function), hash_comp(_Key2, _Key1) is false. The function must impose a total ordering on values of type Key.
The function supplied by hash_compare returns comp(_Key2, _Key1), where comp is a stored object of type Traits that you can specify when you construct the object hash_comp. For the default Traits parameter type less, sort keys never decrease in value.
It was easy to write the hash_compare class for FOO. This question is not for asking how to implement a class. However, it's not straightforward for me that why they have the default trait parameter as less<key> and require total ordering.
hash_map is an unordered data structure. So, I thought that it would be sufficient to have equal_to or not_equal_to instead of less or greater. However, the description of MSDN explicitly states that keys are ordered, which confuses me.
Did I misunderstand the definition of hash_map? Why STL's hash_map actually require orders of its key?
For any value _Key1 of type Key that precedes _Key2 in the sequence and has the same hash value (value
returned by the hash function), hash_comp(_Key2, _Key1) is false. The function must impose a
total ordering on values of type Key.
A total ordering of keys with the same hash value guarantees a total ordering of keys which hash to the same bucket.
That provides the opportunity for a more efficient implementation of search for a key within a particular bucket - e.g. Θ(log n) binary search is possible. If there is no such guaranteed ordering, the worst case (many different keys which are all in the same bucket because they all hash to the same value) is Θ(n).
hash_map that you are looking at is a Microsoft extension that came in in VS2003 and is actually now in stdext in Visual C++ - it's not part of the STL.
std::unordered_map is the official STL version of an associative container with value access by hashable key - the predicate on that is for equality, as you expected.
template<class Key,
class Ty,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, Ty> > >
class unordered_map;
The exact requirements on hash_map vary with the implementation, and some of them (as you've seen) don't make a whole lot of sense. That's part of why they decided not to include a hash_map (or hash_*) in TR1 and/or C++0x. Instead, they have unordered_[multi](map|set), which requires only equal_key, not operator<.
Bottom line: unless you have a truly outstanding reason to do otherwise, use unordered_map instead of hash_map.