Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ compilation error when passing a function into remove_if

So here's a snippet of my code.


void RoutingProtocolImpl::removeAllInfinity()
{
  dv.erase(std::remove_if(dv.begin(), dv.end(), hasInfCost), dv.end()); 
}

bool RoutingProtocolImpl::hasInfCost(RoutingProtocolImpl::dv_entry *entry)
{
  if (entry->link_cost == INFINITY_COST)
  {
    free(entry);
    return true;
  }
  else
  {
    return false;
  }
}

I'm getting the following error when compiling:


RoutingProtocolImpl.cc:368: error: argument of type bool (RoutingProtocolImpl::)(RoutingProtocolImpl::dv_entry*)' does not matchbool (RoutingProtocolImpl::)(RoutingProtocolImpl::dv_entry)'
like image 377
meteoritepanama Avatar asked Apr 15 '10 04:04

meteoritepanama


2 Answers

The Problem

Your predicate RoutingProtocolImpl::hasInfoCost() is a member function. The STL algorithms are dumb in that they can only work with things that feel like regular functions. All the STL algorithms that take an operation or a predicate as parameter invoke them like a function:

op();

This works fine for function objects, pointers to regular functions and pointers to static members. In the case of member functions of reference objects (objects created on the stack in the algorithm's outer scope) or member functions of pointers to objects the function takes an object to be invoked on:

obj.mf();   // reference member function
pobj->mf(); // pointer member function

Now, to resolve the issue you have a number of options.

The Free Function Option

Translate the member function to a free function. If the function needs to work in the context of some object then have that object passed as an extra parameter:

bool hasInfCost(RoutingProtocolImpl::dv_entry *entry,
                const RoutingProtocolImpl& o);

Then, when you pass this function as a reference to an STL algorithm you will have to bind the object parameter:

for_each(cont.begin(), cont.end(),
         bind2nd(hasInfoCost, RoutingProtocolImpl());

The Static Member Option

Change the member function so it is a static member function. You can then pass a reference to it with RoutingProtocolImpl::hasInfoCost. It would make no sense to have the function accept an extra parameter of the encapsulating class' type. If it needs to work in the context of an object then it shouldn't be static: either transform it to a free function (bound to the RoutingProtocolImpl visibility constraints, thus promoting decoupling in your code); or take the binder and adapter approach.

The Binder and Adapter Option

Use a binder and an adapter from the STL to adapt your member function to something the STL algorithms can work with:

dv.erase(remove_if(dv.begin(), dv.end(),
                   bind1st(mem_fun(&RoutingProtocolImpl::hasInfCost),
                           this)),
         dv.end());

This option is arguably the most flexible option of all. You need not change anything in your code, other than the call to the algorithm. The only drawback is that the call to the algorithm will now look somewhat obscure for the uninitiated.

If you choose this path then make sure you get your const-correctness right: if the member function doesn't need to change the object then mark it as a const function. This way you will be able to invoke it with temporary and const objects that you pass to algorithms.

like image 83
wilhelmtell Avatar answered Oct 20 '22 18:10

wilhelmtell


The problem is that this: bool RoutingProtocolImpl::hasInfCost(...) is a nonstatic member function.

It requires an instance of the class to invoke on, ala: obj->hasInfCost(...). However, remove_if cares not and tries to call it as hasInfCost(...). These are incompatible.

What you can do is make it static:

static bool RoutingProtocolImpl::hasInfCost(RoutingProtocolImpl::dv_entry *entry) This no longer requires an instance of the class to invoke. (It has no member variables, no this pointer, etc.). It can be treated as a "normal" function.

like image 39
GManNickG Avatar answered Oct 20 '22 17:10

GManNickG