2

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:

  1. implementation in terms of std::remove_copy_if
  2. must work with function objects and function pointers
  3. without defining helper classes (outside the function scope)
  4. 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));
}
StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • http://stackoverflow.com/questions/1448817/why-there-is-no-stdcopy-if-algorithm – sbi Apr 28 '11 at 21:59
  • @sbi That post doesn't provide a solution that implements copy_if in terms of remove_copy_if and that works with both function pointers and function objects. – StackedCrooked Apr 28 '11 at 22:01
  • 1
    Is there a particular reason you can't just write out the loop in the body of copy_if? Why do you *have* to use remove_copy_if? – AshleysBrain Apr 28 '11 at 22:04
  • @AshleysBrain: I just want to see if it's possible. – StackedCrooked Apr 28 '11 at 22:05
  • @Stacked: IIUC, http://stackoverflow.com/questions/1448817/why-there-is-no-stdcopy-if-algorithm/1448851#1448851 does provide a solution. – sbi Apr 28 '11 at 22:06
  • @sbi yes, but no solution that meets both requirement 1 and 2. – StackedCrooked Apr 28 '11 at 22:09
  • @StackedCrooked: I think there is not going to be a way to satisfy 3 and 4 together; otherwise, why doesn't @sbi's answer satisfy condition 2? Function pointers are function objects. – Jeremiah Willcock Apr 28 '11 at 23:20
  • @Jeremiah the std::remove_copy_if function can handle all three examples: is_odd, is_odd_const_ref and is_odd_functor. I want to achieve the same with the copy_if function. – StackedCrooked Apr 28 '11 at 23:26

1 Answers1

4

Unusually for C++03 <functional>, you're doing slightly too much work. Eliminate Adapter in favor of the factory functions.

http://ideone.com/2VEfH

// Overload that takes a function pointer
template<class ForwardIterator, class OutputIterator, class ArgType>
OutputIterator copy_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, bool (*inPredicate)(ArgType))
{
    return std::remove_copy_if(begin, end, out, std::not1(std::ptr_fun(inPredicate)));
}

// Overload that takes a function object
template<class ForwardIterator, class OutputIterator, class Functor>
OutputIterator copy_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Functor inPredicate)
{
    return std::remove_copy_if(begin, end, out, std::not1(inPredicate));
}

Also, I'll point out that TR1 should be added to the list of now-common facilities you're deciding not to use.

Also, it would be somewhat more elegant if the function-pointer copy_if overload called the functor one, rather than both being complete implementations.

Also, as @wilhelm points out, the return type should be OutputIterator. I've fixed it in this snippet.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • You should return `out` (i.e. return `remove_copy_if()`) – wilhelmtell Apr 28 '11 at 23:35
  • Interesting. However, the provided code results in the same compiler error when calling it with a function pointer that takes a const reference: ` error: forming reference to reference type 'const int&'`. – StackedCrooked Apr 28 '11 at 23:40
  • @Stacked: I'm confused. I fixed the problem with `is_odd_const_ref`, do you have another case that you can post to the question? – Potatoswatter Apr 28 '11 at 23:48