Как назначить разные указатели на функции-члены для разных экземпляров классов регистрации?

РЕДАКТИРОВАТЬ: я ограничен С++ 03 по этой теме.

В следующем коде класс Impl является производным от Intf и содержит экземпляр класса Caller.

ctor Caller принимает экземпляр Intf:: и указатель на функцию-член; он вызывает последний на первом в Caller::func().

ctor Impl регистрирует себя и свою функцию-член func() с содержащимся в ней экземпляром Caller.

Я хотел бы, чтобы Impl содержал несколько экземпляров Caller и регистрировал разные указатели функций-членов для каждого экземпляра, чтобы вызов каждого содержащегося Caller экземпляра ::func() приводил к вызову другой функции-члена Impl - можно ли это сделать?

Единственный способ, которым я мог это сделать, — это определить несколько чистых виртуальных функций в Intf, реализовать их в Impl и зарегистрировать эти переопределяющие функции. Но это не идеальное решение для меня: я хотел бы знать, есть ли способ зарегистрировать разные указатели функций-членов с разными экземплярами класса регистрации без создания виртуальных функций в классе интерфейса 1: 1 с переопределением функций в реализующем классе .

Мне нужно исключить возможность Caller использования ссылки и функции-члена Impl; Caller может знать только о Intfс, а не о Implс.

// main.cpp
#include <iostream>

class Intf
{
public:

  virtual void func() = 0;

  typedef void (Intf::*funcPtr)();
};

class Caller
{
public:

  Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}

  void func() { f_.func(); }

private:

  Intf& f_;
  Intf::funcPtr func_;
};

class Impl : public Intf
{
public:

  Impl()
    : c_ ( *this, &Intf::func )
//    , c2_( *this, static_cast<Intf::funcPtr>(func2) )
  {
  }

  void callCaller() { c_.func(); };

//  void callCaller2() { c2_.func(); };

private:

  void func()
  {
    std::cout << __FUNCTION__ << std::endl;
  }

  void func2()
  {
    std::cout << __FUNCTION__ << std::endl;
  }

  Caller c_;
//  Caller c2_;
};

int main( int argc, char* argv[] )
{
  Impl i;
  i.callCaller();
  return 0;
}

Дополнительный вопрос: может ли кто-нибудь объяснить, почему в ctor Impl необходимо уточнять указатель на функцию-член func() с помощью Intf::? т.е. почему это правильно...

Impl() : c_ ( *this, &Intf::func ) {}

...и почему они неверны...

Impl() : c_ ( *this, &Impl::func ) {}
Impl() : c_ ( *this, &func ) {}

?

Ошибка компилятора в случае версии '&Impl::func':

g++ -g main.cpp && ./a.out

main.cpp: In constructor 'Impl::Impl()':
main.cpp:31:31: error: no matching function for call to 'Caller::Caller(Impl&, void (Impl::*)())'
     : c_ ( *this, &Impl::func )
                               ^
main.cpp:31:31: note: candidates are:
main.cpp:16:3: note: Caller::Caller(Intf&, Intf::funcPtr)
   Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}
   ^
main.cpp:16:3: note:   no known conversion for argument 2 from 'void (Impl::*)()' to 'Intf::funcPtr {aka void (Intf::*)()}'
main.cpp:12:7: note: Caller::Caller(const Caller&)
 class Caller
       ^
main.cpp:12:7: note:   candidate expects 1 argument, 2 provided

Ошибка компилятора в случае версии '&func':

>g++ -g main.cpp && ./a.out
main.cpp: In constructor 'Impl::Impl()':
main.cpp:31:20: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say '&Impl::func' [-fpermissive]
     : c_ ( *this, &func )
                    ^
main.cpp:31:25: error: no matching function for call to 'Caller::Caller(Impl&, void (Impl::*)())'
     : c_ ( *this, &func )
                         ^
main.cpp:31:25: note: candidates are:
main.cpp:16:3: note: Caller::Caller(Intf&, Intf::funcPtr)
   Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}
   ^
main.cpp:16:3: note:   no known conversion for argument 2 from 'void (Impl::*)()' to 'Intf::funcPtr {aka void (Intf::*)()}'
main.cpp:12:7: note: Caller::Caller(const Caller&)
 class Caller
       ^
main.cpp:12:7: note:   candidate expects 1 argument, 2 provided

person StoneThrow    schedule 23.05.2018    source источник
comment
Я рекомендую вам полностью отказаться от использования указателей на функции и вместо этого использовать std::function. .   -  person Some programmer dude    schedule 23.05.2018
comment
Что же касается того, почему вы получаете ошибку, то компилятор ясно говорит об этом: ISO C++ запрещает брать адрес... Другими словами, это запрещено стандартом.   -  person Some programmer dude    schedule 23.05.2018
comment
Забыл отметить: я ограничен С++ 03; Я обновлю пост.   -  person StoneThrow    schedule 23.05.2018
comment
@StoneThrow: Забыл отметить: я ограничен C++03; Просто вау!   -  person JeJo    schedule 23.05.2018
comment
void (Intf::*)() и void (Impl::*)() - это 2 разных типа. Не все Intf будут Impl, поэтому обычно вы не можете вызывать более поздний экземпляр с Intf.   -  person Jarod42    schedule 23.05.2018
comment
Сначала вы описываете ситуацию, которую хотели бы иметь, но не упоминаете конкретные проблемы или сообщения об ошибках. Затем в качестве дополнительных вопросов вы публикуете конкретные ошибки, где вы знаете обходной путь, чтобы вернуться к исходной проблеме. Это, вероятно, отвлечет людей от исходного вопроса, так как с ошибкой компилятора справиться проще, чем с ошибкой «Он не делает то, что я хочу». Я, например, не знаю, в чем конкретная проблема (т.е. сообщение об ошибке) с вашей предполагаемой (и, вероятно, опробованной) реализацией с использованием закомментированного статического приведения   -  person king_nak    schedule 23.05.2018


Ответы (1)


Вы можете использовать обратный вызов старого способа:

typedef void (*callback_t)(void*);

вспомогательная функция:

template <typename C, void (C::*M)()>
void member_func(void* instance)
{
    C* c = static_cast<C*>(instance); // Not typesafe :-/
    ((*c).*M)();
}

А потом

class Caller
{
public:
    Caller(Intf& f, callback_t func) : instance(f), func_(func) {}

    void func() const { func_(&instance); }
private:
    Intf& instance;
    callback_t func_;
};

class Impl : public Intf
{
public:
    Impl()
      : c_  ( *this, &member_func<Intf, &Intf::func> )
      , c2_ ( *this, &member_func<Impl, &Impl::func2> )
  {
  }
// ...
};

Демо

Начиная с C++11, вы можете заменить Caller на std::function<void(void)> и инициализировать его на c2_([=](){ this->func2();}). и назовите его c2_();

person Jarod42    schedule 23.05.2018