Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Functor never be abstract in C++?

In general, templates arguments can be abstract classes, as the program below also shows. But it seems that the compare functor in sort must not be abstract. At least the following does not compile with VC++ 11 and on Oracle Studio 12.

#include <vector>
#include <algorithm>


class Functor
{
public:
    virtual bool operator()(int a, int b) const = 0;
};


class MyFunctor: public Functor
{
public:
    virtual bool operator()(int a, int b) const { return true; }
};


int _tmain(int argc, _TCHAR* argv[])
{
    vector<Functor> fv; // template of abstract class is possible
    vector<int> v;
    MyFunctor* mf = new MyFunctor();
    sort(v.begin(), v.end(), *mf);
    Functor* f = new MyFunctor();
    // following line does not compile: 
    // "Cannot have a parameter of the abstract class Functor"
    sort(v.begin(), v.end(), *f); 
    return 0;
}

Now, I wonder whether this is a general property of functor arguments, or does it depend on the STL implementation? Is there a way to get, what I wanted to do?

like image 757
Kit Fisto Avatar asked Sep 13 '12 14:09

Kit Fisto


People also ask

What is functor in C?

A functor (or function object) is a C++ class that acts like a function. Functors are called using the same old function call syntax. To create a functor, we create a object that overloads the operator().

Why do we need functor?

Functors give you more flexibility, at the cost of usually using slightly more memory, at the cost of being more difficult to use correctly, and at the cost of some efficiency.


3 Answers

Functors generally need to be copyable. Polymorphic base classes are generally not copyable, and abstract bases never.

Update: Thanks to the comments by @ahenderson and @ltjax, here's a very simple way to produce a wrapper object that holds your original, polymorphic reference:

#include <functional>

std::sort(v.begin(), v.end(), std::ref(*f));
//                            ^^^^^^^^^^^^

The result of std::ref is a std::refrence_wrapper which is exactly what you need: A class with value semantics that holds a reference to your original object.


The fact that functors get copied throws off lots of people who want to accumulate something inside the functor and then wonder why the results are off. The functor should really take a reference to an external object. To wit:

Bad! Won't work as you expect; the functor may get copied arbitrarily:

struct Func1 {
    int i;
    Func1() : i(0) { }
    void operator()(T const & x) { /* ... */ }
};

Func1 f;
MyAlgo(myContainer, f); 

Good: You provide the accumulator; it's safe to copy the functor:

struct Func2 {
   int & i;
   Func2(int & n) : i(n) { }
   void operator()(T const & x) { /* ... */ }
};

int result;
MyAlgo(myContainer, Func2(result));
like image 80
Kerrek SB Avatar answered Sep 21 '22 18:09

Kerrek SB


As Kerrek has said you can't do it directly:

But one level of indirection and you're OK.

struct AbstractFunctor
{
  AbstractFunctor( Functor * in_f ): f(in_f) {}
  // TODO: Copy constructor etc.

  Functor * f;
  bool operator()(int a, int b) const { return (*f)(a,b); }
};

int main()
{
  vector<int> v;
  Functor * mf = new MyFunctor();
  sort(v.begin(), v.end(), AbstractFunctor(mf) );
}
like image 21
Michael Anderson Avatar answered Sep 17 '22 18:09

Michael Anderson


As Kerrek and Michael Anderson said, you can't do it directly. As Michael shows, you can write a wrapper class. But there's also one in std:: :

sort(v.begin(),
     v.end(), 
     std::bind(&Functor::operator(),
               mf,
               std::placeholders::_1,
               std::placeholders::_2) );
like image 30
MSalters Avatar answered Sep 19 '22 18:09

MSalters