C++/STL Default comparator - c++

I ma writing a template function that takes a vector of elements and does a sequence of some operations on it. One of these operation is std::sort().
Naturally, client code needs to supply a comparator functor. I do not want the client code to specify a value for this parameter if it passes in a container of well-known types (ints, strings, etc.). How should I define the default value of my Comp template parameter?
template<typename Container, typename Comp=????>
void my_func(Container elements, Comp comp) {
...
std::sort(elements.begin(), elements.end(), comp);
...
}

std::sort uses "Less" as default comparator. So to keep it consistent:
template<typename Container, typename Comp = std::less<typename Container::value_type> >
void my_func(Container& elements, Comp comp = Comp())
{
std::sort(elements.begin(), elements.end(), comp);
}

#include <vector>
#include <algorithm>
template<typename Container, typename Comp=std::less<typename Container::value_type>>
void my_func(Container elements, Comp comp = Comp()) {
//...
std::sort(elements.begin(), elements.end(), comp);
//...
}
int main() {
std::vector<int> v;
my_func(v);
}
Note also Comp comp = Comp()

I wouldn't default the template itself but rather the parameter of the function into something akin to std::greater or std::less for > and < respectively. std::sort uses std::less by default.
If you want to get the template parameter for it then there is a proposal to make std::greater<> to work for generalised types called N3421 that was actually accepted for C++14.
However until then you can do std::greater<typename Container::value_type>. You can opt to remove the reference or cv qualifiers using type_traits if you wish.
<functional> has other default comparisons but those two are the most common.
So a "complete" solution would be something like this:
template<typename Container, typename Comp>
void my_func(Container& elements, Comp comp = std::less<typename Container::value_type>()) {
std::sort(elements.begin(), elements.end(), comp);
}

In your example there is 2 issues:
to make default behavior you must suplly less<T> as a functor.
Your function is taking a copy so, the copy will be sorted, unless you take a refence to Container instead.
Example how to make this:
#include <functional>
template<typename Container, typename Comp=std::less<typename Container::value_type> >
void my_func(Container &elements, Comp comp = Comp() )
{
std::sort(elements.begin(), elements.end(), comp);
}
// A partial specialisation for std::list
#include <list>
template<typename ContainerValueType, typename Allocator, typename Comp=std::less<ContainerValueType> >
void my_func(std::list<ContainerValueType, Allocator> &elements, Comp comp = Comp() )
{
elements.sort(comp);
}

Related

use lambda in function template, can't deduce type, makeSet() use case

What I want to achieve is a makeSet() function accepting three arguments, a pair of iterator, and a function that transforms the value.
One use case could be creating a set from a sequence of values and do transformation, eg, convert a std::map<K,V> to std::set<std::pair<V,K>>.
The client code may look like
auto s = makeSet(hash.begin(), hash.end(),
[](std::pair<int,int> x) { return std::make_pair(x.second, x.first); });
my current attempt is as follow,
// (commented code are some other *failed* attempt).
template <typename Iterator,
typename T = typename std::iterator_traits<Iterator>::value_type,
template<typename ... > class Monad, typename R >
// typename R, typename Monad = std::function<R(T)> >
std::set<R> makeSet(Iterator first, Iterator last, Monad<R,T> f) {
std::set<R> res;
for (; first != last; ++first) res.insert(f(*first));
return res;
}
but unfortunately does not work. The problem looks like failing to deduce R.
Is there any solution or workaround?
I will be very grateful if you can tell me the right way to do it.
The type of a lambda expression is a unnamed class type (its closure type), not std::function. You cannot therefore deduce std::function or Monad from it.
Your best bet would be to do what the standard library does, and simply accept anything as the predicate:
template <
class Iterator,
class UnaryFunction
>
auto makeSet(Iterator first, Iterator last, UnaryFunction f) -> std::set<decltype(f(*first))>
{
std::set<decltype(f(*first))> res;
for (; first != last; ++first) res.insert(f(*first));
return res;
}
Note that you may have to wrap the decltype in std::remove_reference and/or std::remove_cv to cover all corner cases (or, as suggested by #Yakk, std::decay).
Also, to avoid re-inventing the wheel, you might want to take a look at the Boost.Range library.
"The more you overthink the plumbing, the easier it is to stop up the drain." -- Scotty, Star Trek III.
There's no need to over-design the template function, like that. Just use a forwarding reference, and let your C++17 compiler figure everything out.
#include <set>
#include <map>
#include <utility>
#include <type_traits>
// (commented code are some other *failed* attempt).
template <typename Iterator, typename Lambda>
auto makeSet(Iterator first, Iterator last, Lambda &&f) {
typedef typename std::remove_reference<decltype(first->first)>::type const_first_t;
typedef typename std::remove_const<const_first_t>::type first_t;
typedef typename std::remove_reference<decltype(first->second)>::type second_t;
typedef std::pair<first_t, second_t> R;
std::set<R> res;
for (; first != last; ++first) res.insert(f(*first));
return res;
}
void foo()
{
std::map<int, int> m;
std::set<std::pair<int, int>> s =
makeSet(m.begin(), m.end(),
[](const auto &x)
{
return std::make_pair(x.second, x.first);
});
}

Conflict when trying to initialize a priority queue using another comparison functor other than the one declared in the template signature

I implemented a templated priority queue:
template<typename TYPE, typename COMP_FUNCTOR = std::less<TYPE>>
class heap_priority_queue : public priority_queue<TYPE, COMP_FUNCTOR> {
public:
typedef unsigned size_type;
template<typename InputIterator>
heap_priority_queue(InputIterator start, InputIterator end, COMP_FUNCTOR comp = COMP_FUNCTOR());
/* other methods*/
};
template<typename TYPE, typename COMP_FUNCTOR>
template<typename InputIterator>
heap_priority_queue<TYPE, COMP_FUNCTOR>::heap_priority_queue (
InputIterator start, InputIterator end, COMP_FUNCTOR comp) {
for(auto it = start; it != end; ++it) {
data.push_back(*it);
}
fix();
this->compare = comp;
}
The default comparison functor defined in the template signature is std::less, but in the constructor of this priority_queue, it seems we can pass another custom COMP_FUNCTOR comp as the 3rd arguments, or we are using the COMP_FUNCTOR declared in the template signature? But when I tried to pass another COMP_FUNCTOR other than the one declared in the template signature as the third argument conflict happens,why? Here is the code:
class isgreater {
public:
isgreater() {};
bool operator () (int a, int b) {
return a > b;
}
};
int main() {
int my_ints[] = {1, 3, 5, 7, 9};
vector<int> v(my_ints, my_ints + sizeof(my_ints)/sizeof(int));
// wrong, default COMP_FUNCOR is std::less, which is different from isgreater;
heap_priority_queue<int> sq(v.begin(), v.end(), isgreater());
// right, no conflict
heap_priority_queue<int, isgreater> back_up(v.begin(), v.end(), isgreater());
Why should the object we pass in as the third argument of the constructor be the same as the one we declare in the template signature? It seems to me make no sense to keep the third argument of the constructor since we must use the one defined in the template signature...Thank you.
// wrong, default COMP_FUNCOR is std::less, which is different from isgreater;
heap_priority_queue<int> sq(v.begin(), v.end(), isgreater());
is equal to
heap_priority_queue<int, std::less<int>> sq(v.begin(), v.end(), isgreater());
and compilator don't know how convert from isgreater to std::less<>

How to properly declare a generic sorting algorithm?

I am trying to implement the Merge sort algorithm:
#include <list>
#include <functional>
#include <iterator>
#include <iostream>
#include <random>
template <typename TIterator, typename TObject>
void mergeSort(const TIterator& begin, const TIterator& end,
std::function<bool (const TObject& left,
const TObject& right)> criterium)
{
//...
}
bool compare(int a, int b)
{
return a < b;
}
int main(int argc, char** argv) // And now to test the algorithm
{
std::list<int> container;
for (int i = 0; i < 100; ++i)
container.push_back(random() % 20);
mergeSort(container.begin(), container.end(), compare);
for (auto it = container.begin(); it != container.end(); ++it)
std::cout << (*it) << std::endl;
return 0;
}
This program does not compile:
error: no matching function for call to
mergeSort(std::list<int>::iterator, std::list<int>::iterator, bool (&)(int, int))
candidate is:
template<class TIterator, class TObject>
void mergeSort(const TIterator&, const TIterator&,
std::function<bool(const TObject&, const TObject&)>)
at global scope
I know that I messed something simple in my declaration but I cannot figure it out.
The TObject in std::function<bool(TObject const&, TObject const&)> can not be deduced from any argument that is not a std::function already, see this question.
You're also misusing std::function - only use it if you want to store any callable entity. If you just want to take anything callable as a parameter, make it a template parameter:
template<class Iter, class Comp>
void mergeSort(Iter first, Iter last, Comp comp)
{
// use 'comp(a, b)'
}
This is also the way the stdlib does it (see virtually every algorithm with a predicate, like std::sort). This way, you're also avoiding the (in your case unnecessary) type erasure that std::function performs.
You could just take a look at std::sort:
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
http://en.cppreference.com/w/cpp/algorithm/sort
This way you can use whatever callable thing you want to.
Your function compare isn't a std::function but your mergeSort expects one.
Moreover, you should not pass const_iterator to your function because it's supposed to modify the array.
If you change your code and use this:
std::function<bool(const int&, const int&)> myCompare = compare;
mergeSort(container.begin(), container.end(), myCompare);
It works (see http://ideone.com/7FdKTP).
In my opinion, it's easier to implement comparators as structs with an operator(). This way, you pass an object instead of a function, which is easier to manage.
Though I don't know how to completely fix this, it's obvious that the compiler fails to deduce the arguments. Explicitly stating them could work as a workaround:
mergeSort< std::list<int>::iterator, bool >(container.begin(), container.end(), compare);
Another way would be explicitly converting the function you are passing into std::function.
You could also implement this by making the last argument a operator< instead of a comparing function, that would be more intuitive I think.
The mergeSort expects const TIterator and yours isn't.

template argument type deduction from std::function return type with lambda

First of, I'm using C++11 (and my topic sucks).
What I'm trying to do is write a generic template function that implements something usually called sort_by in other programming languages. It involves calculating an arbitrary criterion for each member of a range exactly once and then sorting that range according to those criteria. Such a criterion doesn't have to be a POD, all it has to be is less-than-comparable. For things for which std::less doesn't work the caller should be able to provide her own comparison functor.
I've successfully written said function which uses the following signature:
template< typename Tcriterion
, typename Titer
, typename Tcompare = std::less<Tcriterion>
>
void
sort_by(Titer first, Titer last,
std::function<Tcriterion(typename std::iterator_traits<Titer>::value_type const &)> criterion_maker,
Tcompare comparator = Tcompare()) {
}
It can be used e.g. like this:
struct S { int a; std::string b; double c; };
std::vector<S> s_vec{
{ 42, "hello", 0.5 },
{ 42, "moo!", 1.2 },
{ 23, "fubar", 0.2 },
};
sort_by1< std::pair<int, double> >(
s_vec.begin(), s_vec.end(),
[](S const &one_s) { return std::make_pair(one_s.a, one_s.c); }
);
What I don't like about this approach is that I have to provide the Tcriterion argument myself because the compiler cannot deduce that type from the lambda expression. Therefore this does not work:
sort_by1(s_vec.begin(), s_vec.end(), [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); });
clang 3.1 and gcc 4.7.1 both bark on this (gcc 4.7.1 even barks on the code above, so I guess I'm really doing something wrong here).
However, if I assign the lambda to a std::function first then at least clang 3.1 can deduce the argument, meaning this works:
typedef std::pair<int, double> criterion_type;
std::function<criterion_type(S const &)> criterion_maker = [](S const &one_s) {
return std::make_pair(one_s.a, one_s.c);
};
sort_by1(s_vec.begin(), s_vec.end(), criterion_maker);
So my questions are: How do I have to change my function signature so that I don't need to specify that one argument? And (probably related) how would I fix my example to have it working with gcc?
Don't use std::function in tandem with template argument deduction. In fact, there's very likely no reason to use std::function in a function or function template argument list. More often than not, you should not use std::function; it is a very specialized tool that is very good at solving one particular problem. The rest of the time, you can dispense with it altogether.
In your case you don't need template argument deduction if you use a polymorphic functor to order things:
struct less {
template<typename T, typename U>
auto operator()(T&& t, U&& u) const
-> decltype( std::declval<T>() < std::declval<U>() )
{ return std::forward<T>(t) < std::forward<U>(u); }
// operator< is not appropriate for pointers however
// the Standard defines a 'composite pointer type' that
// would be very helpful here, left as an exercise to implement
template<typename T, typename U>
bool operator()(T* t, U* u) const
{ return std::less<typename std::common_type<T*, U*>::type> {}(t, u); }
};
You can then declare:
template<typename Iter, typename Criterion, typename Comparator = less>
void sort_by(Iter first, Iter last, Criterion crit, Comparator comp = less {});
and comp(*ita, *itb) will do the right thing, as well as comp(crit(*ita), crit(*itb)) or anything else as long as it makes sense.
How about something like this:
template< typename Titer
, typename Tmaker
, typename Tcompare
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker,
Tcompare comparator)
{
typedef decltype(criterion_maker(*first)) Tcriterion;
/*
Now that you know the actual type of your criterion,
you can do the real work here
*/
}
The problem is that you can obviously not use a default for the comparator with this, but you can easily overcome that by providing an overload that doesn't take a comparator and fills in std::less internally.
To do it like you originally suggested, the compiler would have to be able to "invert" the template instantiation process. I.e. for a given std::function<> instantiation, what parameter do I have to supply as the result to get it. This "looks" easy, but it is not!
You can use also something like this.
template< typename Titer
, typename Tmaker
, typename TCriterion = typename
std::result_of
<
Tmaker
(
decltype(*std::declval<Titer>())
)
>::type
, typename Tcompare = std::less<TCriterion>
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker, Tcompare comparator = Tcompare())
{
}
http://liveworkspace.org/code/0aacc8906ab4102ac62ef0e45a37707d

operator overloading and template specialization

I have a template class template<typename T, typename R>. R is of type vector<T*> or list<T*>.
I want my class to overload [] operator so that in case it is a vector I will use the built in [] operator for efficiency and in case it's a list I will implement it with iterator.
To me it sounds like a job for template specialization so I thought to write something like this:
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
//TODO with iterators
}
template<>
T& tContainer_t::operator[]<T, std::vector<T*> >( unsigned i )
{
// TODO with built in [] operator
}
This is wrong and the compiler doesn't allow this.
Is there a way to make it work, or should I use typeid() to differ the two objects at runtime and act accordingly ?
The way to do it with templates is to make a static helper function in a class that can be partially specialized. However, what I would do is:
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
//assuming that the container refernce is name container;
typename R::iterator itr = container.begin();
std::advance(itr, i);
return *itr;
}
std::advance is guaranteed that for a container with random access iterators (such as vector), it is constant time (basically, it does iterator + n), it can be as fast as doing the pointer lookup vector performs. Otherwise, it does iterator++ n times, which will be linear time. The const version will use const_iterator, but is essentially the same.
Doing it this way will let you properly handle different types of containers (not just vector and list), without having to modify the code.
You don't have to overload the operator. The library aleady contains overloaded functions to help you. std::advance will move an iterator, taking advantage of operator+() for random access iterators.
template<typename T, typename R>
T& tContainer_t<T, R>::operator[]( unsigned i )
{
typename R::iterator it = myContainer.begin();
std::advance(it, i);
return *it;
}