Sunday, February 9, 2014

C++ Functor Predicate with std::find_if

struct StringFinder
{
  StringFinder(const std::string & st) : s(st) { }
  const std::string s;
  bool operator()(const RegPair& lhs) const { return lhs.first == s; }
}

std::find_if(sequence.begin(), sequence.end(), StringFinder(foo));


That's not how predicates work. You have to supply either a free function bool Comparator(const MyClass & m) { ... }, or build a function object, a class that overloads operator():

struct MyClassComp
{
  explicit MyClassComp(int i) n(i) { }
  inline bool operator()(const MyClass & m) const { return m.myInt == n; }
private:
  int n;
};

std::find_if(v.begin(), v.end(), MyClassComp(5));

A Functor is a object which acts like a function. Basically, a class which defines operator().

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

The real advantage is that a functor can hold state.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     int operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}


a functor is an object that acts like a function, i.e. it overloads the function call operator.

Functors are commonly used in STL algorithms. They are useful because they can hold state before and between function calls, like a closure in functional languages. For example, you could define a MultiplyBy functor that multiplies it's argument by a specified amount:

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};

Then you could pass a MultiplyBy object to an algorithm like std::transform:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

Another advantage of a functor over a pointer to a function is that the call can be inlined in more cases. If you passed a function pointer to transform, unless that call got inlined and the compiler knows that you always pass the same function to it, it can't inline the call through the pointer.

You can do it with a functor or a regular function that is not part of MyClass, or with a static function inside MyClass - here's an example with non-member function (basically just removing the MyClass:: part of the condition definition):

#include 
#include 

using namespace std;

class Foo
{
  //whatever
};

class MyClass
{
  public:
  int myInt;
  vector foo_v;
};

bool condition(MyClass mc)
{
  if(mc.myInt==5)
    return true;
  else
    return false;
}


int main (void)
{
  vector myClass_v;
  std::find_if(myClass_v.begin(),myClass_v.end(),condition);
}


No comments:

Post a Comment