Say I have this declaration of a priority queue:
struct orderByRewards{
bool operator() (pair<int,pair<int,int> > a, pair<int, pair<int, int> > b){
return a.first < b.first;
}
};
priority_queue<pair<int,pair<int,int> >, vector<pair<int,pair<int,int> > >, orderByRewards> Q;
I was wondering if anybody could take their time and explain me how does the compiler interpret the compare class.
Why do I need to overload the () operator ?
Also, where's the () operator used in its comparing process ?
It feels a little weird, especially since I am not really comfortable with templates and all OOP concepts.
Why do we need to declare the type of a single object and the container type ?
You're essentially asking about function objects (or functors). A function object is one that overloads operator(). You can use such an object as though it were a function. The standard provides a few comparison functors (like your orderByRewards). For example, std::less looks something like this:
template <class T>
struct less {
constexpr bool operator()(const T &lhs, const T &rhs) const
{
return lhs < rhs;
}
};
As we can see, the overloaded operator() just compares the two arguments using < and then returns the boolean result. To use this, you need to create an object of type std::less and then use the function call syntax on it:
std::less<int> compare;
assert(compare(5, 7) == true);
Even though compare is an object, we were able to use it like a function in compare(5, 7).
So now we know that your type orderByRewards is a function object type. You are passing it as a template type argument of std::priority_queue. The implementation of std::priority_queue can then create objects of this comparison function object when it needs to compare elements within the queue.
Consider a simpler example:
template <typename T, typename Comp>
struct foo {
void bar(T a, T b) {
Comp compare;
if (compare(a, b)) {
std::cout << "True" << std::endl;
} else {
std::cout << "False" << std::endl;
}
}
};
It's a silly example, but it gets the point across. We can use this like so:
foo<int, std::less<int>> my_foo;
my_foo.bar(5, 7); // Will print true
We were able to configure foo, by passing it some arbitrary comparison functor type, that its member function bar was able to instantiate and use.
So in the same way, you are configuration std::priority_queue by giving it a comparison functor type that it can use to order elements within the queue. This is how it determines priority between elements. In fact, the default template type argument for std::priority_queue is std::less.
You are not required to overfload the operator(). You can declare your custom method:
typedef bool (*comp)(int,int);
bool compare(int a, int b)
{
return (a<b);
}
int main()
{
std::priority_queue<int,std::vector<int>, comp> pq(compare);
return 0;
}
Updated:
As #WhozCraig pointed out:
It is possible to use this object without overloading the operator() but the compiler will face an easier situation inlining operator() comparison function rather than runtime-provided dereferenced comparison function
Perhaps it would make the most sense to look at the definition of std::priority_queue and work from there.
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> >
class priority_queue {
This says that Compare is some type, and defaults to std::less<T>, for T = the value_type of the underlying container. This compensates for the possibility that you might do something slightly insane like creating a priority_queue of one type, but have the underlying container hold another type (though that's not particularly likely, except by accident).
std::less, in turn, is defined like this:
template <class T> struct less {
bool operator()(const T& x, const T& y) const;
// plus a few typedefs for the argument and return types.
};
In short, it's a type defined with only one publicly available operation: an operator() that can be passed two items that it compares, and returns a bool to indicate whether the first is less than the second.
Since that's what std::priority_queue expects to use, whatever you provide must support essentially the same function call-like syntax and semantics (e.g., the operator() should take const arguments and should itself be const-qualified).
Related
The accepted answer I've seen for swapping out a priority queue comparator is to overload the operator in a new compare class.
class Foo
{
};
class Compare
{
public:
bool operator() (Foo, Foo)
{
return true;
}
};
int main()
{
std::priority_queue<Foo, std::vector<Foo>, Compare> pq;
return 0;
}
However, I want to implement several (10+) different compare functions for queue and choose one at run time when pq is created in main(). Do I have to make 10 different compare classes or is there an easier way to do this?
Do I have to make 10 different compare classes or is there an easier way to do this?
You don't have to. The priority_queue requires that the comparator taking a Foo and return bool - with the default one is std::less
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
In your case, you may use a lambda, or a pointer to function for that purpose. For example,
using cmp1 = bool(*)(const Foo&, const Foo&);
bool FooCmp1(const Foo& f1, const Foo& f2)
{
// do real comparison..
return true;
}
priority_queue<Foo, std::vector<Foo>, cmp1> pq(FooCmp1);
Use a function for comparer template argument, pass the instance of the function to the constructor of the queue, see second constructor there.
For the type, you can use C++ function object std::function<bool(const Foo& a, const Foo& b)>, or C function pointer i.e. bool(*)(const Foo& a, const Foo& b).
In my C++ code I wrote two functors that take to arguments one returns the sum and the other returns the subtract so I could use them as arguments to functions. like this:
template<class T>
class AddValues{
public:
T operator()(const T &value1, const T &value2) {
return value1 + value2;
}
};
template<class T>
class SubstractValues{
public:
T operator()(const T &value1, const T &value2) {
return value1 - value2;
}
};
But now I am looking to write like 6 functors that each one of them takes two arguments and returns true/false wither the first value is <,<=,>,>=,==,!= than the other.
Is there a clearer way to do that rather than defining 6 classes?
I'm working with C++11
Note: This post is a refinement of my comments to the original post.
First, you should be aware that the STL already defines a set of functors. See https://en.cppreference.com/w/cpp/header/functional under Comparators for <,<=,>,>=,==,!=, and Arithmetic operations for +,- (which you have redefined). It is good practice to know the STL and how to use it.
How to use them
Functors are objects like any other object and are to be used with value semantics. For a functional look, they define the function operator (operator()) and can be called () on the object directly.
std::less is_less;
bool is_0_less_than_0 = is_less(0,1); // Calls bool operator()(int, int) and evaluates to true
The functors are usually used in combination with the algorithms. For a not so pretty use case to compare two arrays of integers:
std::array<int,4> low_numbers = {1,2,3,4};
std::array<int,4> high_numbers = {5,6,7,8};
std::array<bool,4> is_number_greater;
// compares low_numbers and high_numbers element wise and stores result in is_number_greater.
std::transform(low_numbers.begin(),
low_numbers_low.end(),
high_numbers.begin(),
is_number_greater.begin(),
std::greater{});
How to write your own functors
So you have already (functionality wise) redefined std::plus (as AddValues) and std::minus (as SubtractValues). Note that I say functionality wise, since it is more flexible to only templatize the function operator:
struct AddValues{
template<class T>
T operator()(const T &value1, const T &value2) {
return value1 + value2;
}
};
And as the member method operator() does not modify any members of AddValues, it should be marked const:
struct AddValues{
template<class T>
T operator()(const T &value1, const T &value2) const {
return value1 + value2;
}
};
Then you do not need to specify the type when instantiating the object. Compare template class:
AddValues<int> add_values; // templated type has to be explicitly written.
add_values(1,2); //=3
with templated method:
AddValues add_values;
add_values(1,2); //=3, types deduced when calling method.
.
Anyway, you would have to do the same for <,<=,>,>=,==,!=, since you need a wrapper around each operator. The difference would only be that now you return booleans instead of a type.
struct MyLess
{
template<typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; }
};
i need to dynamically create arrays containing different numerical types, including char, int, unsigned int, float, double. i'd like to be able to create any two of these arrays and, assuming they're of the same length, implement arithmetic operators such as operator+=
i've been able to implement the array as a template, but i don't know how to implement any arithmetic operators, since i can't know what the other array's type will be at compile time, or even when the first array is created (i will know the type for the array i'm constructing). i looked at std::array, but it doesn't support arithmetic operators. another alternative, which is definitely not elegant (but does work), is to implement a series of type specific operators, such as
MyArray<V> operator+ (const MyArray<float>& addend) const;
MyArray<V> operator+ (const MyArray<double>& addend) const;
MyArray<V> operator+ (const MyArray<int32>& addend) const;
MyArray<V> operator+ (const MyArray<int16>& addend) const;
thanks for any advice.
Alright, its probably obvious enough from my comments of everything in this thread that this is a particular sore spot for me. And this is for good reason, I was once like you. I was like, I can overload operators! AMAZING! OVERLOAD ALL THE OPERATORS (this was with a custom image container type). After a while, a few things became clear:
Operators are hard to correctly declare, especially templated ones.
Templated operators cannot have their types set explicitely, only implicitly.
Operation order doesn't make sense all the time.
Operators must either use exceptions as their "fail" mode which is not ideal in all cases, or use "enable-if" type syntax if the fail can be detected at compile time.
Operator MEANING is hard to document/elucidate. Different interpretations of what an operator should "do" makes it hard to figure out. (Should the MyArray<T>+MyArray<J> work as a memberwise plus like T+J or should it work like a concatenate like 'string+string'?)
Operators must return by value, which can cause overhead if your moves aren't all set up correctly/you aren't in C++11/for any reason return value elision doesn't happen.
Overall, writing your own container types is a great way to redo alot of work the STL has already done.
You COULD do it like (at a namespace scope) (assuming you have a templated cast operator available)
template <typename T, typename J>
MyArray<decltype(T()+J())> operator+(const MyArray<T>& x,const MyArray<J>& y)
{
using K=decltype(T()+J());
MyArray<K> ret(x.size());//or something?
for (size_t i = 0; i < x.size(); i++) {ret[i]=x[i]+y[i];}//could replace with foreach
return ret;
};
Though using the following with vectors just makes more sense. You can wrap it in a "add" call if you want.
std::vector<T> a;//or whatever
std::vector<J> b;//or whatever
std::vector<K> c(a.size());//note: you can still use the decl type here, OR just define it to whatever you actually want it to be
std::transform(a.begin(), a.end(). b.begin(), c.begin(), std::plus<K>());
If you are trying to do this all over the place, and are trying to make a matrix math library, use one like Eigen, it'll save you a lot of work, it'll be strongly typed as a matrix and not a generic collection, and it'll be done with the full math knowledge the Eigen team has.
You can use one more template parameter:
template<class V, class T> MyArray<V> operator+ (const MyArray<T>& addend) const;
Then the cast will always be according to your main array type.
You will likely have to dispatch your operations by a result type selected by some type traits.
Simplified for a number (no vector):
#include <iostream>
template <typename T>
struct Number {
T value;
Number(T value) : value(value) {}
template <typename U>
explicit Number(U value) : value(value) {}
operator T () const { return value; }
};
#define C_PLUS_PLUS_11 (201103L <= __cplusplus)
template <typename U, typename V>
struct binary_operation_traits {
#if C_PLUS_PLUS_11
typedef decltype(U() + V()) result_type;
#endif
};
#if ! C_PLUS_PLUS_11
template <typename T>
struct binary_operation_traits<T, T> {
typedef T result_type;
};
template <>
struct binary_operation_traits<int, float> {
typedef float result_type;
};
template <>
struct binary_operation_traits<int, double> {
typedef double result_type;
};
// And so on ...
#endif
template <typename U, typename V>
Number<typename binary_operation_traits<U, V>::result_type>
operator + (const Number<U>& a, const Number<V>& b) {
typedef typename binary_operation_traits<U, V>::result_type result_type;
return Number<result_type>(result_type(a) + result_type(b));
}
int main ()
{
Number<int> a(1);
Number<double> b(1.5);
std::cout << a + b << '\n';
return 0;
}
I'm having a custom list class and would like to support operations using the "comparison operator" known from the STL. For example:
std::list<MyClass> d;
struct not_key {
not_key( std::string const& str) : str_(str) {}
bool operator( MyClass& elem ) {
return !elem.findThatThing();
}
std::string str_;
};
not_key comp("value");
d.remove_if( comp );
mylist<MyClass> e(d);
e.filter( comp );
And I'm struggling about the signature of a method which accepts these "general" comparison operators. Since all of them have a different type and I don't want static member functions. How can I add a method to my class which accepts the comparison operators?
Thank you very much! :)
If you mean you want to know the signature of mylist::filter you would probably just make it a template with Pred or similar as the type.
template< typename T >
class mylist
{
public:
template< typename Pred >
void filter( Pred pred )
{
// implement, calling pred(elem) for each element or pred(*iter)
}
};
Note that you can pass a free function into that template function, and in C++11 you'll be able to pass in a lambda.
If you want something that isn't a template (other than on the element type) you can use a boost::function (or std::function)
The standard functions (such as std::sort) use a template argument which is deduced to be the type of your comparison function-like object:
template <class UnaryPredicate>
void filter(UnaryPredicate func) {
// Call it like:
func(something);
}
Now UnaryPredicate will be deduced to be the type of whatever function-like object you pass to it. UnaryPredicate makes more sense than calling it a comparison function since it only takes a single argument. A comparison function would typically take two arguments and compare them.
Alternatively, you could take a std::function<bool(const MyClass&)>:
void filter(std::function<bool(const MyClass&)> func) {
// Call it like:
func(something);
}
The signature should be:
bool operator()(Myclass const & elem) const
A std::map must satisfy the requirements of an associative container specified in paragraph 23.1.2/2:
Each associative container is
parameterized on Key and an ordering
relation Compare that induces a strict
weak ordering (25.3) on elements of
Key. In addition, map and multimap
associate an arbitrary type T with the
Key. The object of type Compare is
called the comparison object of a
container. This comparison object may
be a pointer to function or an object
of a type with an appropriate function
call operator.
But then in paragraph 23.3.1/2 the std::map template is specified as:
template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class map;
which seems to explicitly prohibit the use of a function pointer as Compare. Is this a contradiction or am I not understanding the standard correctly?
EDIT: Yes, the problem I was really having was why code like GMan's example:
struct foo
{
int x;
};
bool compare_foo(const foo& x, const foo& y)
{
return x.x < y.x;
}
std::map<foo, bool, compare_foo> fooMap;
wouldn't compile (yeah, I stupidly mixed up the type and value of the Compare parameter).
Compare is the type of the comparator. The fact that it is declared with class instead of typename doesn't make a difference, you can have a pointer to function as type and give your function in the map constructor.
#include <map>
bool myCmp(int a, int b) {
return a < b;
}
void foo()
{
std::map<int, char*, bool (*)(int, int)> m(myCmp);
}
This:
class Compare
does not mean that Compare has to be a class. It might be clearer if it said:
typename Compare
You can use any type that provides the comparable function call semantics, such as a function pointer.
Specify it like this:
struct foo
{
int x;
};
bool compare_foo(foo x, foo y)
{
return x.x < y.x;
}
// vvvvvvvvvvvvvvvvv function pointer type
std::map<foo, bool, bool(*)(foo, foo)> fooMap(compare_foo);
// function pointer value ^^^^^^^^^^^
You are confusing the type and the value of the comparator; use this e.g.:
int main()
{
std::map<foo, bool, bool(*)(const foo&, const foo&)> fooMap(compare_foo);
}