I couldn't find a way to set a custom comparator function for QMap, like I can for std::map (the typename _Compare = std::less<_Key> part of its template arguments).
Does QMap have a way to set one?
It's not documented (and it's a mistake, I think), but in you can specialize the qMapLessThanKey template function for your types (cf. the source). That will allow your type to use some other function rather than operator<:
template<> bool qMapLessThanKey<int>(const int &key1, const int &key2)
{
return key1 > key2; // sort by operator> !
}
Nonetheless, std::map has the advantage that you can specify a different comparator per each map, while here you can't (all maps using your type must see that specialization, or everything will fall apart).
No, as far as i know QMap doesn't have that functionality it requires that it's key type to have operator<, so you are stuck with std::map if you really need that compare functionality.
QMap's key type must provide operator<(). QMap uses it to keep its items sorted, and assumes that two keys x and y are equal if neither x < y nor y < x is true.
In case, overload operator<().
Related
In C++, the std::set::insert() only inserts a value if there is not already one with the same 'value'. By the same, does this mean operator== or does it mean one for which operator< is false for either ordering, or does it mean something else?
does it mean one for which operator< is false for either ordering?
Yes, if the set uses the default comparator and compares keys using <. More generally, in an ordered container with comparator Compare, two keys k1 and k2 are regarded as equivalent if !Compare(k1,k2) && !Compare(k2,k1).
Keys are not required to implement operator== or anything else; they are just required to be comparable using the container's comparator to give a strict weak ordering.
std::set has a template argument called `Compare' as in this signature:
template < class Key, class Compare = less<Key>,
class Allocator = allocator<Key> > class set;
Compare is used to determine the ordering between elements. Here, the default less<Key> uses the < operator to compare two keys.
If it helps, you can think of a set as just a std::map with meaningless values, ie a std::set<int> can be thought of as a std::map<int, int> where the values are meaningless.
The only comparison that set is allowed to perform on T is via the functor type it was given to do comparisons as part of the template. Thus, that's how it defines equivalence.
For every value in the set, the comparison must evaluate to true for one of the two ordering between that value and the new one. If it's false both ways for any value, then it won't be stored.
According to cplusplus.com, the std::type_info::before() function...
Returns true if the type precedes the type of rhs in the collation order.
The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order.
So what is it useful for?
Consider you want to put your type_info objects as keys into a map<type_info*, value>. The type_info doesn't have an operator < defined, so you must provide your own comparator. The only thing that is guaranteed to work from the type_info interface is the before() function, since neither the addresses of type_info nor the name() must be unique:
struct compare {
bool operator ()(const type_info* a, const type_info* b) const {
return a->before(*b);
}
};
std::map<const type_info*, std::string, compare> m;
void f() {
m[&typeid(int)] = "Hello world";
}
This is useful to define an order on typeinfo objects, e.g. to put them into a std::map. The obvious follow-up question is: why isn't it spelled operator<()? I don't know the answer to this question.
It gives an ordering.
That is required if you want to store values in some containers, like std::map.
Think of it as less-than (<) operator for type_info objects. If you ever wanted to store in ordered collection - such a set of map - you can use it to make an appropriate comparator. It's a reliable and preferred way, as opposed to, say, using type's name which might not be unique.
I have encountered a problem that I want to define a map, that is sorted internally by the first's descending order. if the first is not a primary type, like if it's a class, I can just overload the "<" within that class, but I don't know how to deal with the int type. Any suggestion?
Thanks a lot!!
Add a comparator:
#include <functional>
map<int, value_type, greater<int>> m;
The default is less<int>.
You can specify a comparator when you create the map (it's an optional constructor argument).
e.g.:
bool my_compare(int x, int y) { return y < x; }
std::map<int,T,bool(*)(int,int)> my_map(my_compare);
Notice that I've needed to explicitly specify a 3rd template parameter as well.
[Note: I would strongly advise that you don't overload the < operator to perform >...]
Look at the std::less implementation: http://www.cplusplus.com/reference/std/functional/less/
You may write own comparator and use it together with map.
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.
I have a std::multimap where key is a custom class. Something like this:
Class X {
public:
std::string s;
int x;
operator <(const X& other) const { return s < other.s; }
};
std::multimap<X, int> mymap;
Now, I'd like to use upper_bound and lower_bound to iterate over all elements with the same value of "s". Do I need to implement some other operator for X (for example: ==). Or it will work properly just like this?
Also, what should I supply as argument for upper_bound and lower_bound? I assume I should create a dummy object with desired value of "s"?
Since class X is the key for the multimap, the parameter to upper_bound()/lower_bound() needs to be of that type. If class X has an implicit conversion from std::string (which is the type of X::s) then you can use that as the parameter to upper_bound()/lower_bound().
The default comparison for multimap is less<> which simply calls operator <() - so that's the only operator you a required to have in class X for the multimap to work.
you only need to provide an operator == and <.
upper_bound and lower_bound are just like any other find-type method, so you need the same kind of object to compare with - in your case, a 'dummy' object with the required value of s.
edit: the comments are correct that you only need operator< for lower/upper_bound, and find. But if you want to call other methods on your container, you will need operator== as well. Eg. if you want to sort() your container, you will need operator==.
The 2 overloads you need for all STL containers are operator< and operator==. I find its best practise to implement them both.
Of course, the question could also be answered more fully by implementing a comparison functor in the map itself, not relying on the objects. This is often a good way to implement different ways of calling find() on the map.