can't initialize functor objects when passing derived class in C++

我的未来我决定 提交于 2019-12-04 16:03:44

Here's a modification of my original answer that seems to do what you need, without using the any functor stuff from C++11 or boost.

#include <vector>
#include <map>
#include <string>

struct Command {};
struct Subtract : Command {};
struct Add : Command {};

class CommandFactory
{
  public:

    virtual Subtract * createSubtractionCommand() = 0;
    virtual Add * createAdditionCommand() = 0;
};

class StackCommandFactory : public CommandFactory
{
  public:

    virtual Subtract * createSubtractionCommand(void);
    virtual Add * createAdditionCommand(void);

    Subtract * sub;
    Add * add;
};

Subtract * StackCommandFactory::createSubtractionCommand(void) { return sub; }
Add * StackCommandFactory::createAdditionCommand(void) { return add; }

class CommandGetterImpl
{
  public:
    virtual CommandGetterImpl* clone() const=0;
    virtual Command* get()=0;
    virtual ~CommandGetterImpl() {};
};

class CommandGetter
{
  public:
  Command* get() { return impl_->get(); }
  ~CommandGetter() { delete impl_; }
  CommandGetter( const CommandGetter & other ) : impl_(other.impl_?other.impl_->clone():NULL) {}
  CommandGetter& operator=( const CommandGetter & other ) {
     if (&other!=this) impl_= other.impl_?other.impl_->clone():NULL;
     return *this;
  }
  CommandGetter() : impl_(NULL) {}
  CommandGetter( CommandGetterImpl * impl ) : impl_(impl) {}
  CommandGetterImpl * impl_;
};

class Parser
{
  public:
    Parser (CommandFactory & fact);

    std::map<std::string, CommandGetter > operations; 
};

template<typename MEMFN, typename OBJ >
class MemFnCommandGetterImpl : public CommandGetterImpl
{
  public:
  MemFnCommandGetterImpl(MEMFN memfn, OBJ *obj) : memfn_(memfn), obj_(obj) {}
  MemFnCommandGetterImpl* clone() const { return new MemFnCommandGetterImpl( memfn_, obj_) ; }
  Command* get() { return (obj_->*memfn_)(); }
  MEMFN memfn_;
  OBJ * obj_;
};


template< typename MEMFN, typename OBJ >
CommandGetter my_bind( MEMFN memfn, OBJ * obj )
{
  return CommandGetter( new MemFnCommandGetterImpl<MEMFN,OBJ>(memfn,obj) );
};

Parser::Parser(CommandFactory & fact)
{
  operations["+"] = my_bind(&CommandFactory::createAdditionCommand, &fact);
  operations["-"] = my_bind(&CommandFactory::createSubtractionCommand, &fact);
}

#include <iostream>
int main()
{
  Add add;
  Subtract sub;

  StackCommandFactory command_factory;
  command_factory.add = &add;
  command_factory.sub= &sub;
  Parser parser(command_factory);

  std::cout<<"&add = "<<&add<<std::endl;
  std::cout<<"Add = " <<  parser.operations["+"].get() <<std::endl;
  std::cout<<"&sub = "<<&sub<<std::endl;
  std::cout<<"Sub = " <<  parser.operations["-"].get() <<std::endl;

  return 0;
}

You need an explicit cast on the 2nd parameter of the bar instance:

Function<CommandFactory> bar(&fact,
  reinterpretet_cast<Command *(CommandFactory::*)()>(&CommandFactory::createAdditionCommand));

Besides, you're missing parens for the method pointer attribute in Function:

Command * (T::*fptr)();

This error might have prevented you to find the solution above.

You are also missing the return keyword in the operator() there (a mistake that I often do because of my functional programming habits):

virtual Command * operator()(){
   return  (obj->*fptr)();
}            

You can avoid the cast by making the return type a template parameter:

template <class T, typename D> 
class Function : public Functor {
private:
    D * (T::*fptr); 
    T* obj;
public:
    Function(T* obj, D * (T::*fptr)()): obj(obj),  fptr(fptr){}
    virtual Command * operator()(){
        return (obj->*fptr)();
    }            
};

void Foo(CommandFactory & fact){
    Function<CommandFactory, Addition> bar(&fact, &CommandFactory::createAdditionCommand);
}

Note that I did not templatize Functor after all. While it seemed a good idea at first to me, it make things a bit more complex. If you wish to make Functor a template too, the return type will have to be exactly the same, you cannot use an inheritance relation between them, unless you make them both parameters of the Function template. As a rule of thumb, whenever you stumble on a template issue like that, remember that template are like C macros at the core, it's a rewriting mechanism, which will expand the template into real C++ types (functions or classes) separately. You can picture the problem that way:

template <typename T, typename D>
class Function : public Functor<D> { /* ... */ };

will be expanded to

class Function<CommandFactory, Addition> : public Functor<Addition> {
    /* ... */
};

Functor<Addition> and Functor<Command> bears no relationship at all; these are two different classes.

If C++ template did carry the notion of bounded polymorphism (like in Java or C#), it could have perhaps been possible to write it in way close to your intent.

I recommend:

  • keeping the Functor a simple class, to make the code simpler to work with for the time being, and
  • if the need arises later on, trying to refactor a working version with that new feature.

Generally speaking, it's a bad idea to use member function pointers as opposed to std::function. More generally,

typedef std::function<void()> Command;
typedef std::function<Command()> Functor;

Really, there's absolutely no need whatsoever for any member function pointers in your code.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!