Question

I have vector of objects and a vector of criteria to filter by. Inspired by Moo-Juice from this post, I wrote such a code:

#include <algorithm>
#include <string>
#include <memory>
#include <vector>

struct Token {

  char code;
   int val;

  Token(char c,int a) : code(c),val(a) {}
};

class FilterBase {
public:
  virtual bool operator()(const std::shared_ptr<Token> p) =0;
};

class ByCode : public FilterBase {
public:
  ByCode( char c) : code_(c) {}

  virtual bool operator()(const std::shared_ptr<Token> p) {
    return code_ == p->code;
  }

private:
  unsigned char code_;
};


int main() {

  std::vector<std::shared_ptr<FilterBase>> filters;
  filters.push_back(std::make_shared<ByCode>('A'));
  filters.push_back(std::make_shared<ByCode>('B'));

  std::shared_ptr<Token> p = std::make_shared<Token>('M', 20);
  std::vector<std::shared_ptr<Token>> tokens;
  tokens.push_back(p);
  filters[0]->operator ()(p);

  for (const std::shared_ptr<FilterBase> fi : filters) {
    tokens.erase(std::remove_if(tokens.begin(), tokens.end(), *fi), tokens.end());
  }
}

But unfortunately it does not compile, because parameter type 'FilterBase' is an abstract class. Well I know it is, I just thoght the virtual keyword would make it working...

Was it helpful?

Solution

Replace:

tokens.erase(std::remove_if(tokens.begin(), tokens.end(), *fi), tokens.end());

with:

tokens.erase(std::remove_if(tokens.begin(), tokens.end(), std::ref(*fi)), tokens.end());

remove_if takes its functor by-value, which causes your *fi to be sliced into an instance of the base class, which has a pure virtual object. Things go poorly.

std::ref has an overloaded operator() which should invoke the virtual operator() of *fi if MSVC didn't screw things up (for example, if it invokes the operator the wrong way).

If this fails, you can write your own adapter:

template<typename T>
struct callable_by_ref {
  T* t;
  template<typename... Args>
  auto operator(Args&&...args) const ->
  decltype( std::declval<T&>()(std::declval<Args&&>()...) )
  {
    return (*t)(std::forward<Args>(args)...);
  }
};
template<typename T>
callable_by_ref< typename std::remove_reference<T>::type >
call_by_ref( T&& t ) {
  return {&t};
}

which should solve your problem even if std::ref does not.

tokens.erase(std::remove_if(tokens.begin(), tokens.end(), call_by_ref(*fi)), tokens.end());

What I am doing:

The callable_by_ref is basically a quick perfect forwarder with a half-functional std::reference_wrapper implementation on top of it. call_by_ref does type deduction for you to create such an object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top