I have worked out a solution that works most of the time:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
// Overload that takes a function pointer
template<class ForwardIterator, class OutputIterator, class ArgType>
void copy_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, bool (*inPredicate)(ArgType))
{
typedef std::pointer_to_unary_function<ArgType, bool> Adapter;
std::remove_copy_if(begin, end, out, std::unary_negate<Adapter>(Adapter(inPredicate)));
}
// Overload that takes a function object
template<class ForwardIterator, class OutputIterator, class Functor>
void copy_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Functor inFunctor)
{
std::remove_copy_if(begin, end, out, std::unary_negate<Functor>(inFunctor));
}
bool is_odd(int inValue)
{
return inValue % 2 == 1;
}
bool is_odd_const_ref(const int & inValue)
{
return inValue % 2 == 1;
}
struct is_odd_functor : public std::unary_function<int, bool>
{
bool operator() (const int & inValue) const { return inValue % 2 == 1; }
};
int main()
{
std::vector<int> numbers;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
std::vector<int> copy;
// Functor: Ok
copy_if(numbers.begin(), numbers.end(), std::back_inserter(copy), is_odd_functor());
// Function pointer: Ok
copy_if(numbers.begin(), numbers.end(), std::back_inserter(copy), is_odd);
// Function pointer that takes const ref: Compiler error
copy_if(numbers.begin(), numbers.end(), std::back_inserter(copy), is_odd_const_ref);
return 0;
}
The only situation where it doesn't work is when the function pointer takes a const ref argument. This results in the following compiler error:
/usr/include/c++/4.2.1/bits/stl_function.h: In instantiation of ‘std::unary_negate<std::pointer_to_unary_function<const int&, bool> >’:
main.cpp:11: instantiated from ‘void copy_if(ForwardIterator, ForwardIterator, OutputIterator, bool (*)(ArgType)) [with ForwardIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, OutputIterator = std::back_insert_iterator<std::vector<int, std::allocator<int> > >, ArgType = const int&]’
main.cpp:53: instantiated from here
/usr/include/c++/4.2.1/bits/stl_function.h:322: error: forming reference to reference type ‘const int&’
/usr/include/c++/4.2.1/bits/stl_algo.h: In function ‘_OutputIterator std::remove_copy_if(_InputIterator, _InputIterator, _OutputIterator, _Predicate) [with _InputIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OutputIterator = std::back_insert_iterator<std::vector<int, std::allocator<int> > >, _Predicate = std::unary_negate<std::pointer_to_unary_function<const int&, bool> >]’:
main.cpp:11: instantiated from ‘void copy_if(ForwardIterator, ForwardIterator, OutputIterator, bool (*)(ArgType)) [with ForwardIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, OutputIterator = std::back_insert_iterator<std::vector<int, std::allocator<int> > >, ArgType = const int&]’
main.cpp:53: instantiated from here
/usr/include/c++/4.2.1/bits/stl_algo.h:1227: error: no match for call to ‘(std::unary_negate<std::pointer_to_unary_function<const int&, bool> >) (int&)’
Apparently a reference to a reference is attempted to be made here.
My question is: How can I implement a copy_if
that meets the following conditions:
- implementation in terms of std::remove_copy_if
- must work with function objects and function pointers
- without defining helper classes (outside the function scope)
- using pure C++ and STL (no C++0x or boost)
Update
I assume that this is possible because it works with boost::bind
:
template<class ForwardIterator, class OutputIterator, class Functor>
void copy_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Functor inFunctor)
{
std::remove_copy_if(begin, end, out, !boost::bind(inFunctor, _1));
}