Defining custom map comparator with non stardard signature - c++

Assuming that there is a map that has a pointer as a key. To deep compare of the underling object it's possible to write custom comparator in following way:
#include <map>
#include <memory>
bool compare(std::unique_ptr<int> lhs, std::unique_ptr<int> rhs){
return *lhs<*rhs;
}
int main(){
std::map<std::unique_ptr<int>, short, bool (*)(std::unique_ptr<int>, std::unique_ptr<int>)> elements(compare);
}
That's undestandable, but why would I need to explicitly pass type of the compare callback as it is imposed by the type of map key? Why it's not possible create a map in following way as the signature of it should be known:
std::map<std::unique_ptr<int>, short> elements(compare);
Are there any cases when it's needed to define a compare function with different signature?
EDIT:
The last question I think I already answered to myself. At some point I could write something like:
#include <map>
#include <memory>
bool compare(std::unique_ptr<BaseClass> lhs, std::unique_ptr<BaseClass> rhs){
return *lhs<*rhs;
}
int main(){
std::map<std::unique_ptr<DerivedClass>, short, bool (*)(std::unique_ptr<BaseClass>, std::unique_ptr<BaseClass>)> elements(compare);
}
But it does not explain why by default exact signature is not used.

Why it's not possible create a map in following way as the signature of it should be known
But it's not known uniquely. The function is a valid initializer for at least two types of maps
std::map<std::unique_ptr<int>, short, bool (*)(std::unique_ptr<int>, std::unique_ptr<int>)>
std::map<std::unique_ptr<int>, short, bool (&)(std::unique_ptr<int>, std::unique_ptr<int>)>
A pointer or a reference to a function works equally well here. Neither is inherently better, so how's the type to be determined uniquely? There's no immediately compelling reason to favor one over the other.
It's also worth mentioning that the slightly more idiomatic use of a std::map is backwards. I.e. instead of figuring out the type from a comparator object, a user-defined type that can initialize objects is used instead. For example:
struct compare {
bool operator()(std::unique_ptr<int> lhs, std::unique_ptr<int> rhs){
return *lhs<*rhs;
}
};
std::map<std::unique_ptr<int>, short, compare> elements;

The type of the comparator is not at all determined by the type of the key. The comparator doesn't have to be a function, it could be a class overloading operator().
In fact, the third template parameter has a default argument that is an instantiation of just such a class, namely std::less<Key>. That is, std::map<std::unique_ptr<int>, short> is a shorthand for std::map<std::unique_ptr<int>, short, std::less<std::unique_ptr<int>>>

Related

std::is_const & Co with value-type wrappers as std::reference_wrapper

This might be a bit of an academic example (in the sense that I don't see it having a real use case as-is), but I have come across this line of thought a couple of times without coming up with a satisfying answer.
For the sake of argument, let's suppose that I have a template function that I want to behave differently depending on whether the passed value is const or not. A very simple example might be
template< typename T > void print_constness(T&& t) {
if constexpr (std::is_const_v< decltype(t) >) {
std::cout << "T is const\n";
} else {
std::cout << "T is NOT const\n";
}
}
If I pass a mutable reference to this function, it will correctly detect it as non-const. If I pass a const reference to it, then it correctly detects it as const (provided I can prevent the function from making a copy, e.g. by deleting the copy constructor).
Conceptually, std::reference_wrapper< T > is supposed to represent the same type as const T &. Therefore, one might expect that the result from passing a const T to that function is the same as passing a std::reference< const T >.
But this is not the case, since the wrapper itself is not const. However, for practical purposes, it is. Consider e.g. a template function that has to call a const or non-const overload of a function. When passed a std::reference_wrapper< const T >, it will pass it to the non-const overload and as soon as that tries to access the reference the compiler will complain (rightfully so).
(Note that I deliberately ignored that you can overload on the constness of your argument - my above example shall only serve as an illustration).
My question is: How to detect and in further steps modify constness of value-type wrappers such as std::reference_wrapper when the standard std::is_const, std::add_const and std::remove_const clearly don't work?
Is there a generic/standard solution to this problem or would it require implementing custom is_const, ... traits that specialize on the value wrappers that one expects to encounter?
If so: is it perhaps possible for the implementers of such wrappers to specialize the std type traits so they produce the (semantically) expected result? I kinda expect this to be forbidden...
If you have C++20, there's std::unwrap_reference:
#include <type_traits>
template<typename T>
using remove_reference_and_wrapper_t =
std::remove_reference_t<std::unwrap_reference_t<std::remove_reference_t<T>>>;
template<typename T>
constexpr static bool is_semantically_const_v =
std::is_const_v<remove_reference_and_wrapper_t<T>>;
static_assert(is_semantically_const_v<const int>);
static_assert(is_semantically_const_v<const int&>);
static_assert(is_semantically_const_v<const int&&>);
static_assert(!is_semantically_const_v<int>);
static_assert(!is_semantically_const_v<int&>);
static_assert(!is_semantically_const_v<int&&>);
static_assert(is_semantically_const_v<std::reference_wrapper<const int>>);
static_assert(!is_semantically_const_v<std::reference_wrapper<int>>);
It's a little unwieldy, but it works.
You can then use the type returned by remove_reference_and_wrapper_t to further manipulate the object; i.e., get a reference to the actual object:
remove_reference_and_wrapper_t<decltype(t)>& underlying = t;

How is std::map implemented so it can require its key_type to be comparable?

This is my implementation of the Box class:
class Box {
friend ostream& operator<<(ostream &os, const Box &b);
friend bool operator<(const Box &left, const Box &right);
public:
Box(int i, double d);
~Box();
private:
int i;
double d;
};
Box::Box(int _i, double _d):i(_i), d(_d) {}
Box::~Box() {}
bool operator<(const Box &left, const Box &right)
{
return (left.i < right.i);
}
ostream& operator<<(ostream &os, const Box &b)
{
os << b.d;
return os;
}
This the test code:
int main()
{
Box b1(3,2), b2(2,1), b3(0, 9);
map<Box, int> bmap;
bmap.insert(pair<Box,int>(b1, 10));
bmap.insert(pair<Box,int>(b2, 10));
bmap.insert(pair<Box,int>(b3, 10));
for (map<Box,int>::iterator iter = bmap.begin(); iter != bmap.end(); ++iter)
{
cout << iter->first << " ";
}
cout << endl;
return 0;
}
If I remove the definition of operator< on the Box class, the compiler will complain (an error) if I try to insert a Box object into std::map.
I have some experience with Java and I know in similar cases I just have to let Box implement Comarable. And Java compiler will check this contract at compile time, because Map in Java requires its key type conform to Comparable.
And if I want to define my own map type in Java, I just need to write:
public class MyMap<K extends Comparable<K>, V>
So my question is, if I want to implement my own map type (say, MyMap) in C++, how to define MyMap so that the compiler knows at compile time that "MyMap requires its key_type has its own overloaded definition of operator<"?
Long story short, you don't have to do anything: write your code as if the operator is there.
Unlike Java generics, C++ template mechanism can work without constraints, because the compiler is not required to produce any code until all class parameters are fully specified. In contrast, Java compilers must fully compile the class, and produce the final byte code without knowing the types that you plug in for K and V.
In other words, C++ compiler lets you call any functions and apply any operators you want in your template code. The template will compile without a problem if the classes that you supply have the corresponding functions and/or operators. If the functions and/or operators referenced from the template are missing, the compiler gives you an error message.
You do not need to specify any constraints in your generic type, like comparable in Java. By just using operator < in your templated class, makes this a requirement.
So in C++ you would just write:
template<typename K, typename V>
class MyMap {
..
if(a < b) {
..
}
What happens as soon as you instantiate a template, for example by writing MyMap<string, string> the compiler creates a new class by substituting K and V with string. If you put a type in without operator<, this will create a compile error.
Look at http://en.cppreference.com/w/cpp/container/map:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
The reason your compiler complaints about a missing '<'-operator is that the Compare-object std::less<Key> want's it. The keys are 'sorted by using the comparison function Compare', see C++ std::map key sort comparison function? for more information about how to implement your 'own' Compare-object. Usually you won't need to do this because the <-operator is implmented for fundamental types already (ints, floats etc) and for other types it is implemted as part of the STL:
https://sourceforge.net/p/stlport/code/ci/master/tree/stlport/stl/_string_operators.h#l347
template <class _CharT, class _Traits, class _Alloc>
inline bool _STLP_CALL
operator<(const basic_string<_CharT,_Traits,_Alloc>& __x,
const basic_string<_CharT,_Traits,_Alloc>& __y) {
return basic_string<_CharT,_Traits,_Alloc> ::_M_compare(__x.begin(), __x.end(),
__y.begin(), __y.end()) < 0;
}
Note: the Compare-object is not only used to sort the maps, but also determines if a key is considered 'existant in the map':
Internally, the elements in a map are always sorted by its
key following a specific strict weak ordering criterion indicated
by its internal comparison object (of type Compare).
And:
Compare:
A binary predicate that takes two element keys as arguments and returns
a bool. The expression comp(a,b), where comp is an object of this type
and a and b are key values, shall return true if a is considered to go
before b in the strict weak ordering the function defines.
The map object uses this expression to determine both the order the
elements follow in the container and whether two element keys are equivalent
(by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)).
No two elements in a map container can have equivalent keys.
This can be a function pointer or a function object (see constructor for an
example). This defaults to `std::less<Key>`, which returns the same as applying the
less-than operator (a<b).
Aliased as member type map::key_compare.
(see http://www.cplusplus.com/reference/map/map/ ) Another good source of information is SGI's documentation of their STL-implementation: https://www.sgi.com/tech/stl/Map.html
Again, since in these docs are a lot of words and you would need to read them very very carefully:
they are equivalent if !comp(a,b) && !comp(b,a)
So, (since it felt onto my toes onces) you can construct a map<struct my*, int, my_cmp> where the my_cmp compare-function decides that 2 pointers of type my are NOT equal, allthough they are the same value:
struct my* a = &my_a;
struct my* b = a;
The output of my_cmp() decides, if a given key (and the associated value) are stored in the map or not. Very subtle.
Maybe interesting to read: https://latedev.wordpress.com/2013/08/12/less-than-obvious/ and http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/
Think of a template as an expression that can be used to generate code, not as code itself (that's actually how the templates got their names, prior to C++ templates some people would abuse the preprocessor to achieve the same goal). That is, when you write
template<class T> void foo(const T& bar) {
baz(bar);
}
it's pretty much the same as if you had written
#define foo(bar) baz(bar)
The contents of the definition (template or preprocessor) is pretty much irrelevant as long as it's not used. Only when the template is instanciated / the preprocessor directive is expanded, the compiler will check whether the result of the instanciation/expansion is valid.
As such, when a template uses a certain member function or operator on one of its arguments, it is the job of the user to supply a type that can be used in such a way, otherwise the compiler will do the substitution, look at the resulting code, shake its head, and throw an error message.

How do I compare two generic types in C++?

I need to determine whether an element is the same as the one I'm passing by reference.
In the function Belongs I need to compare equality between d is and an element of a stored in a dynamic list:
struct Nodo{ Dominio dominio; Rando rango; Nodo* next; };
typedef Nodo* ptrNodo;
ptrNodo pri;
template<class Dominio, class Rango>
bool DicListas<Dominio,Rango>::Belongs(const Dominio &d)
{
bool retorno = false;
if(!EsVacia())
{
ptrNodo aux=pri;
while(aux!=NULL)
{
if(aux->dominio==d)//-------> THIS CLASS DOESN'T KNOW HOW TO COMPARE THE TYPE DOMINIO.
{
retorno = aux->isDef;
}
aux = aux->sig;
}
}
return retorno;
}
Whatever type argument you provide for the type parameter Dominio, you've to overload operator== for that type.
Suppose, you write this:
DicListas<A,B> obj;
obj.Belongs(A());
then you've to overload operator== for the type A as:
class A
{
public:
bool operator == (const A &a) const
{
//compare this and a.. and return true or false
}
};
Also note that it should be public if it's a member function, and better make it const function as well, so that you can compare const objects of type A.
Instead of making it member function, you can make operator== a non-member function as well:
bool operator == (const A &left, const A & right)
{
//compare left and right.. and return true or false
}
I would prefer the latter.
It reduces to defining an overload of operator== for the user-defined type:
bool operator==(const WhateverType &a, const WhateverType &b)
{
return whatever;
}
or maybe as a member of WhateverType.
If you want to compare something about two, possibly distinct, types, you probably want to look at either Boost type traits or the versions of type traits that made it into TR1 and C++11 (if you're using a compiler that supports either TR1 or C++11).
However, that doesn't seem to be the case that you're running into. In your case, you know that the two objects are of the same type. In C++, you will get compiler errors if a class you pass as a type parameter to a template does not support all the methods or operators that the template needs. That's what you're running into. That's also the problem that concepts are meant to solve (well, concepts are meant to advertise "if you want to use your type with this template, then your type must support ..."). But, unfortunately we didn't get concepts in C++11, so the requirements are implicit. In your case, as already mentioned, you simply need to make sure that whatever class you pass in as Dominio supports operator==.
You may also want to look at Boost concept check to advertise that whatever type is passed in as Dominio must support operator==.

C++ how to pass method as a template argument

Suppose I have a class X:
class X {
// ...
size_t hash() const { return ...; }
};
I would like to create a std::tr1::unordered_map<X, int, HashFn> where I want to pass in
X::hash() as HashFn. I know I can declare my own functor object. I feel that
there should be a way to do this by directly passing a pointer to X::hash().
Is there?
No; as you've shown it, you need a small utility struct:
#include <functional>
template<typename T, std::size_t (T::*HashFunc)() const = &T::hash>
struct hasher : std::unary_function<T, std::size_t>
{
std::size_t operator ()(T const& t) const
{
return (t.*HashFunc)();
}
};
Then you can create an unordered_map like so:
std::tr1::unordered_map<X, int, hasher<X> > m;
No, there isn't. The reason is that whatever is used as your HashFn must take a single argument which is a const reference to an object in the container. X::hash takes a single argument which is a const pointer to an object in the container (the this pointer is an implicit first argument in this case), so using that function by it self is not possible.
You probably use some bind magic, using boost::lambda and boost::bind. I'm not exactly sure how, but it would probably look something like this:
boost::bind(&X::hash, &_1);
Which creates a function object which will call X::hash with a pointer.
size_t hash() const { return ...;}
A function which calculates hash value takes one parameter of type Key which your function doesn't take. Hence its signature is wrong to begin with.
Since you want to implement a function, rather than a functor, here is how it should be done:
size_t hash(const KeyType &key)
{
return /*calculate hash and return it*/;
}
Make it static member function and pass it as X::hash or make it a free function, is your choice.
You can't directly, but you can wrap it. The easy way to do so is to use boost::mem_fn(), or the standard equivalents if your compiler supports them: tr1::mem_fn() (from TR1) or std::mem_fn() (from C++11).
EDIT: Actually it's not so simple. mem_fn() will work fine for a function parameter, but since its return type is unspecified it's difficult to use as a template parameter. If you have C++11 support you could use decltype to find the type; otherwise you're probably best off writing your own function object as you mentioned.

Returning a priority_queue with custom comparator

I have a function that needs to return a sorted list based on some input parameters. I've selected a std::priority_queue to hold this list.
But the compiler is giving me an error I don't recognize. Here's the code I have:
struct DepthCompare {
bool operator()
(const struct inst *&lhs, const struct inst *&rhs) const
{
return lhs->depth < rhs->depth;
}
};
typedef priority_queue<struct inst*> HeuristicList;
HeuristicList getHeuristicList(struct BasicBlock &) {
HeuristicList ret( DepthCompare );
return ret;
}
The compiler says that a conversion from 'HeuristicList (*)(DepthCompare)' to non-scalar type 'HeuristicList' requested on the return statement's line.
It doesn't look like I'm trying to return a pointer. What's going wrong?
You have two problems.
To use a custom comparator, you must specify the comparator type as the third template argument:
typedef priority_queue<inst*, vector<inst*>, DepthCompare> HeuristicList;
HeuristicList ret( DepthCompare ); is interpreted as a function declaration, rather than a variable declaration, giving the error that you're seeing. You need to pass an instance of the comparator, and make sure it can't be interpreted as a function declaration:
HeuristicList ret = HeuristicList(DepthCompare());
However, since the constuctor's first argument is optional, and defaults to a default-constructed comparator, you can simply write
HeuristicList ret;
Or, since you're just returning the variable straight away,
return HeuristicList();
Note that the comparator is the third template parameter of priority_queue. You must declare your priority_queue like such:
typedef priority_queue<inst*, vector<inst*>, DepthCompare> HeuristicList;
This assumes you want to use vector as the backing container (default).
Also note that in your comparator functor, you want to declare the parameters as const reference to a pointer. What you have is a reference to a pointer to const. You want this:
bool operator()(inst* const& lhs, inst* const& rhs) const
You also don't need to pass an instance of your comparator object to the priority_queue constructor, as the default comparator constructor will do just fine.