How to properly declare a generic sorting algorithm? - c++

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.

Related

C++ functors behavior

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
struct cmp {
bool operator()(const int& i, const int& j) const{
return false;
}
} ;
struct cmp2 {
bool operator()(const int& i, const int& j) const{
return false;
}
} cmp2_item;
class Solution {
public:
vector<int> smth(vector<int> arr, int k) {
// nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp); #ERROR
// nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp()); #WORKS
// nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp2_item); # WORKS
// sort(arr.begin(), arr.end(), cmp); #ERROR
// sort(arr.begin(), arr.end(), cmp()); #WORKS
// set<int, cmp> s; # WORKS
// set<int, cmp2_item> s; # ERROR
return {};
}
};
int main() {
// your code goes here
Solution s;
s.smth({}, 1);
return 0;
}
I want to understand why this code behaves in this way.
for nth_element we expect a functor so it makes sense to include like cmp(), but why does it start working without the (), when i instantiate the struct and use that?
Similar in sort
while when using it for a comparator for a set, it only works if the struct is not instantiated and without the ()
Can someone please clarify using the signatures why this is so?
nth_element: template< class RandomIt, class Compare > void nth_element ( RandomIt first, RandomIt nth, RandomIt last, Compare comp );
sort
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
set:
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
One part of this is that cmp2_item is not a type, its an instance of the type cmp2. So you can't pass that as a class type. You might be able to do:
set<int, cmp> s; //# WORKS
set<int, decltype(cmp2_item)> s2; //# NOW WORKS
For these:
// Not ok because cmp is not a function comparison object, its a type
nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp); #ERROR
// Ok because this is effectively passing a functor (and not just a type)
// I believe this takes a copy of the functor type (by construction), I
// am not quite so clear on the inner workings of the compiler in this
// case. I guess its by type comparison, but possible compiler
// implementation specific?
nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp()); #WORKS
// This is ok because it passes an actual comparison object (that has
// operator () defined).
nth_element(arr.begin(), arr.begin()+k, arr.end(), cmp2_item); # WORKS
Basically you have to look more closely at what you are passing: a type, an object or a function - and what the specific STL accepts as a parameter.
Note:
comparison function object (i.e. an object that satisfies the requirements of Compare) which returns ​true if the first argument is less than (i.e. is ordered before) the second.
See here: enter link description here

Declare a container const or non-const depending on iterator

Suppose that I have the following function:
template <typename Iterator>
void f()
{
std::list<int> numList;
Iterator it = numList.begin();
...
}
Iterator can be either std::list<int>::iterator or std::list<int>::const_iterator.
The above function compiles for std::list<int>::iterator, but for std::list<int>::const_iterator the list should be declared as const std::list<int>.
Is there a way to declare the type of the list either std::list<int> or const std::list<int> depending on whether Iterator is iterator or const_iterator?
It should be possible by using std::iterator_traits, <type_traits> and std::conditional, something similar to
using pointer_type = typename std::iterator_traits<Iterator>::pointer;
using list_type = typename std::conditional<std::is_const<pointer_type>::value, const std::list<int>, std::list<int>>::type;
list_type numList;
Mind this is untested so you should probably adjust it a little bit, it's just to give you the basic idea behind this. Please check this answer too.

wrapping template function and <unresolved overloaded function type

I have problem with my wrapping function.
template <typename Iter, typename SomeFunction>
void wrap(Iter first, Iter last, SomeFunction someFunction)
{
someFunction(first, last);
}
I would like to use it like this:
template <typename Iter>
void fill5(Iter first, Iter last)
{
fill(first, last, 5);
}
int main()
{
vector<int> v(100, -1);
wrap(v.begin(), v.end(), fill5);
}
But I get
test.cpp: In function ‘int main()’:
test.cpp:16:40: error: no matching function for call to ‘wrap(std::vector<int>::iterator, std::vector<int>::iterator, <unresolved overloaded function type>)’
test.cpp:16:40: note: candidate is:
wrap.h:6:6: note: template<class Iter, class SomeFunction> void wrap(Iter, Iter, someFunction)
I know that if I will call that function like this
wrap(v.begin(), v.end(), fill5< vector<int>::iterator> );
it will compile. But do I always have to do it explicit? It's sucks. Why compiler can't deduce which function will be used? Is there any possibility to write wrap function to take arguments like in the first one?
In C++03 or C++11 there isn't much you can do about it.
fill5 is a function template, and you cannot take the address of a function template. A function template is regarded by the compiler as an infinite overload set, and requires you to specify explicitly which instance you want to take the address of:
wrap(v.begin(), v.end(), fill5<vector<int>::iterator>);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alternatively (as I mentioned in a comment to the original post, and as other answers suggest) you could use a functor with a templated call operator to wrap the invocation of fill5.
In C++14, however, we can do something better: we can use generic lambdas to simplify things a bit. If you define this macro:
#define WRAP(f) \
[&] (auto&&... args) -> decltype(auto) \
{ return f (std::forward<decltype(args)>(args)...); }
You can then write:
int main()
{
std::vector<int> v(100, -1);
wrap(v.begin(), v.end(), WRAP(fill5));
}
Here is a live example.
An alternative is to use a functor:
class fill5
{
public:
template <typename Iter>
void operator () (Iter first, Iter last) const
{
std::fill(first, last, 5);
}
};
int main( int, char ** )
{
std::vector<int> v(100, -1);
wrap(v.begin(), v.end(), fill5);
return 0;
}
Because fill is a template function, there are basically an infinite number of overloads and the compiler does not know which one to choose.
If you declared your second template parameter to describe a function that took 2 parameters of type Iter it could then deduce it. The example below works and looks like what you want. It is not quite as generic as your attempt. It ensures the function to be called returns void and takes 2 Iter paramters.
template <typename Iter >
void wrap( Iter first, Iter last, void(*someFunction)(Iter,Iter) )
{
someFunction( first, last );
}
template <typename Iter>
void fill5(Iter first, Iter last)
{
fill(first, last, 5);
}
int main( int, char ** )
{
std::vector<int> v(100, -1);
wrap(v.begin(), v.end(), fill5);
return( 0 );
}

C++/STL Default comparator

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);
}

overloading of template function error C2688

i can't compile the following code
namespace sequential_sort
{
template<class T>
void sort(std::list<T>& source)
{
sort(source.begin(), source.end()); //(1)
}
template<class Iter>
void sort(Iter begin, Iter end)
{
if(begin == end)
return;
typedef Iter::value_type value_type;
value_type value = *(begin);
Iter part = std::partition(begin, end, [&value](const value_type&->bool{return t < value;});
sort(begin, part);
Iter divide = part;
divide++;
sort(divide, end);
}
}
It says that at line (1) I have error C2688 ambiguous call to overloaded functions.
I can't understand why, overloaded functions even have different number of parameters?
There are a few issues at play:
1) You need to declare your single parameter sequential_sort::sort function before the two parameter one, unless you want to another sort function. You probably don't see the effect of this because you are using a Windows C++ compiler, which is not standards compliant in this respect. What should have happened is that std::sort was unambiguously picked, and that wouldn't compile because it requires random access iterators (std::list has a sort member function which you should use instead of std::sort).
2) Once that is fixed, since you are passing std::list iterators to the two parameter sort, argument dependent lookup (ADL) means std::sort is considered too. This is the cause of the ambiguity. You can fix this by being specific about which two-parameter sort you want to use:
#include <list>
#include <algorithm>
namespace sequential_sort
{
template<class Iter>
void sort(Iter begin, Iter end) { }
template<class T>
void sort(std::list<T>& source)
{
sequential_sort::sort(source.begin(), source.end()); //(1)
//^^^^^^^^^^^^^^^
}
}