C++ functors behavior - c++

#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

Related

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<>

Splitting std::vector based on some criteria

I have a vector which contain some data. I want to split it into const number of vectors depending on some criteria. For example:
using Point=std::pair<int,int>;
std::array<std::vector<Point>,4> split_to_4(const std::vector<Point>& data,std::function<size_t(Point)> criteria);
int main(){
std::vector<Point> data;
//fill data
auto results=split_to_4(data,[](const Point& p){
if(cond1) return 0;
if(cond2) return 1;
if(cond3) return 2;
return 3;
});
}
What is the best way to implement split_to_4? My current attempt is:
std::array<std::vector<Point>,4> split_to_4(const std::vector<Point>& data,std::function<size_t(Point)> criteria){
std::array<std::vector<Point>,4> result;
for (const auto& p : data){
areas_regions[criteria(p)].emplace_back(p);
}
return result;
}
Any better.. More std way to do it?
By Better, I mean: more readable... depend on iterator... depend on some std functions...
You can do this in place with multiple calls to std::partition:
// Returns iterators to the three partition points in the range
template<class ForwardIt, class Which>
auto split4(ForwardIt first, ForwardIt last, Which which) {
std::array<ForwardIt, 3> ret;
ret[0] = std::partition(first, last,
[&](const auto &v){return which(v) == 0;});
ret[1] = std::partition(ret[0], last,
[&](const auto &v){return which(v) == 1;});
ret[2] = std::partition(ret[1], last,
[&](const auto &v){return which(v) == 2;});
return ret;
}
Of course you can also pass and use the conditions directly instead of proxiing through some which function if you so desire.
One could also trivially rewrite this with a loop to generalize it to splitN if necessary. (Watch out though, the complexity of this approach is O(N * n) for a range with n elements. This will probably be unreasonably slow for big N. On the other hand, we get swaps instead of copies, which may help if copying is expensive (compared to calling which). If performance is critical, measure.)
If you need the relative order of elements in each group preserved, std::stable_partition is your friend.
Just noticed the C++11 tag: The above code is C++14. For C++11 compatibility, simply change the autos I used to the explicit types, i.e. use std::array<ForwardIt, 3> as the return type and const std::iterator_traits<ForwardIt>::value_type& for the lambdas.
I'll leave that as is for brevity, this last paragraph completes the answer for the pre-C++14 folks.
update:
probably the most STL-like way:
Features:
Iterator-based so choice of source and destination containers is left to the caller
Source iterators may be move-iterators if move-partitioning is required, or leave as normal iterators to make a copy
Linear time complexity
Stable ordering of results (ref std::stable_partition)
-
#include <array>
#include <vector>
#include <utility>
#include <cassert>
using Point=std::pair<int,int>;
// example split function - could be a function object
extern std::size_t which_bucket(const Point&);
template<class Iter, class OutIter, class Which>
auto split_n(Iter first, Iter last,
OutIter outfirst, std::size_t N,
Which&& which)
{
while (first != last) {
auto index = which(*first);
assert (index < N);
std::next(outfirst, index) -> push_back(*first);
++ first;
}
}
template<class Iter, class OutIter, class Which>
auto split_to(Iter first, Iter last,
OutIter outfirst, OutIter outlast,
Which&& which)
{
return split_n(first, last, outfirst,
std::distance(outfirst, outlast),
std::forward<Which>(which));
}
int main(){
std::vector<Point> source;
std::array<std::vector<Point>, 4> dest { };
split_n(source.begin(), source.end(),
dest.begin(), dest.size(),
which_bucket);
// or
split_to(source.begin(), source.end(),
dest.begin(), dest.end(),
which_bucket);
// or with move request:
split_to(std::make_move_iterator(source.begin()),
std::make_move_iterator(source.end()),
dest.begin(), dest.end(),
which_bucket);
}
another way
#include <array>
#include <vector>
#include <utility>
using Point=std::pair<int,int>;
// example split function - could be a function object
extern std::size_t which_bucket(const Point&);
template<class Iter, class Which>
auto split4(Iter first, Iter last, Which&& which)
{
std::array<std::vector<Point>, 4> result {};
while (first != last) {
result[which(*first)].push_back(*first);
++first;
}
return result;
}
int main(){
std::vector<Point> data;
auto results = split4(data.begin(), data.end(), which_bucket);
}
Here's another way which honours any custom allocator in the vector:
#include <array>
#include <vector>
#include <utility>
using Point=std::pair<int,int>;
// example split function - could be a function object
extern std::size_t which_bucket(const Point&);
template<class T, class A, class Which>
auto split4(const std::vector<T,A>& v,
Which&& which)
{
using vec_type = std::vector<T,A>;
std::array<std::vector<T,A>, 4> result {
vec_type(v.get_allocator()),
vec_type(v.get_allocator()),
vec_type(v.get_allocator()),
vec_type(v.get_allocator())
};
for (auto& p : v) {
result[which(p)].push_back(p);
}
return result;
}
int main(){
std::vector<Point> data;
auto results = split4(data, which_bucket);
}

how to pass a predicate to algorithm

I'm having a problem passing predicate using lambda, I'm trying to move element that matches the predicate to the beginning of a second container, but it didn't seem to work, so what's wrong please?
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <utility>
#include <algorithm>
using namespace std;
template <typename iterator, typename Container, typename T>
void move_if(iterator b, iterator e, Container o, T pred)
{
if(pred)
{
o.insert(o.begin(),pred);
}
}
int main()
{
vector<int>v{1,2,3,4,5,6,7,8,9,10};
vector<int>v2;
for (auto i=v.begin(); i !=v.end(); ++i)
save_if(v.begin(), v.end(), v2, []( vector<int>::iterator i){return (*i>5);});
return 0;
}
Try this...
int main()
{
std::vector<int> v{1,2,3,4,5,6,7,8,9,10};
std::vector<int> v2;
std::vector<int>::const_iterator
it = std::remove_copy_if(v.begin(), v.end(),
std::back_inserter(v2),
[](int const& i){return i <= 5;});
v.erase(it, v.end);
return 0;
}
You can read more about remove_copy_if on cppreference.com; it removes elements from the input range and copies them to the output unless the predicate returns true.
Note that this is an STL remove, so you need to call erase afterwards to shrink the input. The semantics of this solution are slightly different to the code you posted, but more similar to your description of what you wanted.
Check this out, I did some modifications on your code:
template <typename iterator, typename Container, typename T>
void move_if(iterator a, iterator b, Container &o, T pred)
{
for (auto i = a; i != b; i++)
{
if (pred(*i))
o.insert(o.begin(), *i);
}
}
int main()
{
vector<int>v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int>v2;
move_if(v.begin(), v.end(), v2, [](int i) { return !(i > 5); });
}
Note: As the comments, It's recommended to rename move_if to copy_if, if the functionality is as above code, otherwise you should really move items.
There is no overload of std::vector::insert that takes a predicate as second argument, so this line is wrong:
o.insert(o.begin(),pred);
Furthermore, the predicate needs to be called with an argument,
pred(someArg);
which in your case would be an std::vector<int>::iterator. Also, save_if is not the same as move_if. But more importantly, it isn't clear at all what you are trying to achieve.
In C++11, stateless lambdas like [](){return true} that do not capture anything can be implicitly converted to function pointers. When you do if(pred) you are converting your stateless lambda into a function pointer, checking if that pointer is non-null (it is non-null). This is not what you want to do.
Here is an implementation that moves things between b and e that pred(x) says should be moved:
template <typename iterator, typename Container, typename T>
void move_if(iterator b, iterator e, Container o, T pred)
{
for( auto i = b; i != e;++i) {
if(pred) {
o.insert(o.end(),std::move(*i));
}
}
}
Note that I inserted at o.end(), because the Container you want is probably vector, and inserting at the end() of vector is much faster.
In reality, you probably want to take an output iterator (and by default, use std::back_inserter from a Container) and output your data to that. Similarly, remove_move_if would be a better way to remove, shuffling the elements down the b-e range, and returning an iterator.
Finally, ranged-based algorithms are worth writing. Instead of taking a begin/end iterator pair, take a single object upon which begin(c) and end(c) have been overriden to return begin/end. If you are working on a sub-range, you can pass in a begin/end range of iterators struct with begin/end suitably overridden.

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.

C++ writing an assign procedure for a container

I have a container, a class, and I would like to provide it with a method assign like STL do.
class myclass {
public:
//...
template < typename InputIterator >
void assign(InputIterator first, InputIterator last);
// ...
private:
// ...
std::vector<int> mystlcont;
// ...
};
template < typename InputIterator >
myclass::assign(InputIterator first, InputIterator last) {
this->mystlcont.clear();
this->mystlcont.assign(first, last);
}
Ok. Well I compile, ok.
I use it now...
myclass m;
std::vector<int> vect;
vect.push_back(1);
vect.push_back(1);
vect.push_back(2);
vect.push_back(3);
vect.push_back(5);
vect.push_back(8);
vect.push_back(13);
m.assign(vect.begin(), vect.end()); // Line X
When calling function at line X, compiler returns a very very ugly error.
workflow_test.hpp: In function ‘void
middleware::WorkflowSerializationTest()’:
workflow_test.hpp:114: error: invalid
cast from type
‘__gnu_cxx::__normal_iterator
’ to type ‘middleware::TaskDescriptor*’
workflow_test.hpp:114: error: invalid
cast from type
‘__gnu_cxx::__normal_iterator
’ to type ‘middleware::TaskDescriptor*’
workflow_test is the file where I am calling the function assign, myclass repreents Workflow class in workflow.hpp and workflow.cpp... TaskDescriptor is the element in the vector (the type in the collection in myclass).
Do you know why this happens?
I suspect that there must be some operator to overload in my class to let this mechanism work correctly.... becaue it is not method implementation the problem, even if I write
template < typename InputIterator >
void myclass::assign(InputIterator first, InputIterator last) {
// NOTHING WRITTEN IN THE BODY
}
When called raises the same exact "exception".
What you have is not your own container, but rather container wrapper.
But I see only one problem with your code: your myclass::assign definition does not have a return type specified.
This code actually works for me:
#include <vector>
#include <algorithm>
#include <iostream>
class MyWrapper {
public:
template <typename InputIterator>
void assign(InputIterator first, InputIterator last);
const std::vector<int>& container() const { return container_m; }
private:
std::vector<int> container_m;
};
template <typename InputIterator>
void MyWrapper::assign(InputIterator first, InputIterator last)
{
container_m.assign(first, last);
}
template <typename T>
void print(const T& x)
{
std::cout << x << " ";
}
int main()
{
MyWrapper mw;
std::vector<int> vect;
vect.push_back(1);
vect.push_back(2);
vect.push_back(3);
vect.push_back(4);
vect.push_back(5);
vect.push_back(6);
vect.push_back(7);
mw.assign(vect.begin(), vect.end());
std::for_each(mw.container().begin(), mw.container().end(), print<int>);
}
Output:
1 2 3 4 5 6 7