не удается инициализировать объекты-функторы при передаче производного класса В C++


Этот вопрос вытекает из предыдущего вопроса, который я задал здесь. Я не могу использовать никакие внешние библиотеки или спецификацию C++ 11. Это значит, что я не могу использовать std::связать, с std::функция, повышение::связать,тип Boost::функция etc. Я должен написать его сам. Проблема заключается в следующем:

Рассмотрим код:

EDIT

Вот полная программа, которая показывает проблему в соответствии с запросом:

#include <map>
#include <iostream>


class Command {    
public:
    virtual void executeCommand() = 0;
};

class Functor {
public:
    virtual Command * operator()()=0; 
};

template <class T> class Function : public Functor {
private:
    Command * (T::*fptr); 
    T* obj;                  
public:
    Function(T* obj, Command * (T::*fptr)()):obj(obj),
        fptr(fptr) {}

    virtual Command * operator()(){
        (*obj.*fptr)();   
    }            
};

class Addition:public Command {
public:
    virtual void executeCommand(){
        int x;
        int y;
        x + y;
    }
};

class CommandFactory {
public:
    virtual Addition * createAdditionCommand() = 0;
};

class StackCommandFactory: public CommandFactory {
private:
    Addition * add;
public:
    StackCommandFactory():add(new Addition()) {}

    virtual Addition * createAdditionCommand(){
         return add;
    }
};

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

int main() {
    StackCommandFactory fact;
    Foo(fact);
    return 0;
}

Ошибка, которую он дает, является "no instance of constructor "Function<T>::Function [with T=CommandFactory] matches the argument list, argument types are: (CommandFactory *, Addition * (CommandFactory::*)())

Я думаю, что он жалуется, потому что я передача ему производного типа. Я должен использовать указатели / ссылки на абстрактные классы, потому что fact может не быть StackCommandFactory позже по дороге.

Я не могу сказать:

void Foo(CommandFactory & fact){
      Function<CommandFactory> spf(&fact,&fact.createAdditionCommand); //error C2276

}

Из-за этого я получаю ошибку C2276, которая говорит (как в вопросе, который я связал) '&' : illegal operation on bound member function expression.

Таким образом, мой вопрос явно звучит так: "как мне инициализировать этот объект функтора, чтобы я мог использовать его с вышеупомянутыми интерфейсами?"

3 3

3 ответа:

Вот модификация моего оригинального ответа, которая, кажется, делает то, что вам нужно, без использования каких-либо функторов из C++11 или 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;
}

Требуется явное приведение ко 2-му параметру экземпляра bar:

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

Кроме того, вы пропускаете parens для атрибута указателя метода в Function:

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

Эта ошибка могла помешать вам найти решение выше.

Вы также пропускаете ключевое слово return в operator() там (ошибка, которую я часто делаю из-за моих привычек функционального программирования):

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

Вы можете избежать приведения, сделав возвращаемый тип шаблоном параметр:

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);
}

Обратите внимание, что я не templatize Functor в конце концов. Хотя поначалу это казалось мне хорошей идеей, теперь все стало немного сложнее. Если вы также хотите сделать Functor шаблоном, тип возвращаемого значения должен быть точно таким же, вы не можете использовать отношение наследования между ними, если вы не сделаете их оба параметра шаблона Function. Как правило, всякий раз, когда вы натыкаетесь на такую проблему шаблона, помните, что шаблон похож на макросы C в основе, это механизм перезаписи, который будет разворачивать шаблон в реальные типы C++ (функции или классы) отдельно. Вы можете представить себе эту проблему следующим образом:

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

Будет расширено до

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

Functor<Addition> и Functor<Command> не имеет никакого отношения вообще; это два различных класса.

Если бы шаблон C++ действительно нес понятие ограниченного полиморфизма (как в Java или C#), возможно, было бы возможно написать его способом, близким к вашему намерению.

I рекомендую:

  • сохраняя Functor простой класс, чтобы сделать код более простым для работы в настоящее время, и
  • если позже возникнет необходимость, попробуйте рефакторинговать рабочую версию с этой новой функцией.

Вообще говоря, это плохая идея использовать указатели на функции-члены в отличие от std::function. В более общем плане,

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

Действительно, нет абсолютно никакой необходимости в указателях функций-членов в вашем коде.