Идиома именованных параметров с использованием указателя на частный метод класса

Я застрял с ошибкой компиляции C ++, когда делал что-то, что, вероятно, не совсем "обычное". Чтобы упростить задачу, я просто переписал механизм, который я пытаюсь использовать, в более удобном для чтения виде, и я проверил, что у меня такая же проблема.

Прежде всего, вот код:

test.h // - - C ++ - -

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp;}
  init() {}
  virtual ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  item(init<MODULE> params) : m_fp(params.m_fp) {}
  virtual ~item() {}
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Method CB
  int func1(int i);
  // Item member
  item<user> m_item;
public:
  user();
  virtual ~user();
};

test.cpp // - - C ++ - -

#include "test.h"

user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {}

int user::func1(int i) {return 1;}

и вот ошибка:

/test.cpp:5:59: error: invalid use of non-static member function
 user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {
                                                       ^

Итак, я не уверен, что это лучший способ достичь того, чего я хочу (вероятно, нет, в любом случае, если у вас есть другие предложения, они очень приветствуются), но моя цель сейчас - заставить его работать или точно понять, почему это не может работать так что я чему-то научился!

Основная идея заключается в том, что:

  • класс «item» может быть инициализирован с идиомой именованного параметра с использованием метода «has_funcPtr» класса «init», присоединенного к его конструктору, например: «init (). has_funcPtr (& function_name)».
  • класс «пользователь» может хранить указатель на свой частный метод «func1» как частный член своего частного члена типа «элемент».

Таким образом, когда вызывается конкретный метод объекта "item" (для простоты я не включаю здесь эту длинную часть, поскольку она не имеет отношения к ошибке, а просто описывает цель этого фрагмента кода) этот метод может делать что-то и вызывать частный метод своего родительского объекта "user" через указатель на функцию (я надеюсь, что это достаточно ясно ...).

Теперь я думаю, что есть проблема с порядком инициализации объектов, но я не уверен, где и как это исправить. В частности, я подумал, что, поскольку метод "func1" не работает ни с одним членом класса "user", его ссылка может использоваться непосредственно в списке инициализации для инициализации объекта "init" и передачи его в "item". " объект.

Спасибо всем заранее


comment
Комментарий, если кто-то в будущем захочет повторно использовать этот код. В методе has_funcPtr в классе инициализации я забыл вернуть сам объект. Это должно быть: init & has_funcPtr (funcPtr fp) {m_fp = fp; return * this;}   -  person Bertone    schedule 20.10.2015


Ответы (2)


this->func1 не образует указатель на функцию-член. Он должен выглядеть как &user::func1, если вы находитесь в классе user.

person Bo Persson    schedule 20.10.2015
comment
Спасибо, это невероятно, что мне удалось потратить столько времени на что-то столь простое ... Но еще кое-что. Если я использую & user :: func1, использую ли я указатель на метод этого экземпляра? Потому что вначале я подумал, что просто has_funcPtr (func1) должен работать, поскольку я уже нахожусь в области действия пользователя, и, в случае, если я хотел получить доступ к методу экземпляра, я должен был каким-то образом использовать указатель this. - person Bertone; 20.10.2015
comment
Указатель функции-члена не привязан к конкретному экземпляру, поэтому вам также нужно будет объединить его с экземпляром при использовании указателя. См. Как вызвать функцию указателя на член? < / а> - person Bo Persson; 20.10.2015

Я размещаю здесь полный ответ на мою проблему. Я разработал его после предложения Бо и после того, как понял, как указать на конкретный метод экземпляра с помощью указателя на него.

Короче говоря, действительно важно отметить две вещи:

  1. Указатель на нестатическую функцию-член класса можно рассматривать как просто смещение, а не как «абсолютный адрес» (http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm ). Это означает, что вы не можете получить доступ к этой функции (это просто смещение) без предварительного указателя экземпляра. Когда у вас есть указатель экземпляра, с помощью этого «указателя смещения» вы можете вызвать этот метод, используя:

    (object_ptr->*method_ptr)(parameters_here)

    Лучшим способом было бы использовать макрос #define, поскольку этот синтаксис действительно подвержен ошибкам и сложен для чтения (https://isocpp.org/wiki/faq/pointers-to-members):

    #define CALL_MEMBER_FN(ptrToObject,ptrToMember) ((ptrToObject)->*(ptrToMember))

    а затем используйте его как:

    CALL_MEMBER_FN(object_ptr, method_ptr)(parameters_here)

  2. Следуя первому пункту, если вы хотите, чтобы вложенный класс мог вызывать метод верхнего класса с помощью указателя на него, вам также необходимо передать указатель экземпляра верхнего класса для доступа к этой функции. В моем случае, поскольку я хотел иметь возможность решать в каждом конкретном случае, следует ли вызывать этот метод или нет, я использовал идиому именованных параметров (например, обратите внимание, что func2 не зарегистрирован).

Наконец, вот исправленный код, который работает (протестирован):

- - C ++ - - test.h

#include <iostream>

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE* m_dad;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp; return *this;}
  init& has_func2Ptr(func2Ptr fp2) { m_fp2 = fp2; return *this;}
  init(MODULE* dad) : m_dad(dad) { std::cout << "init constructor called\n"; }
  ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE*  m_dad;
public:
  item(init<MODULE> params) :
    m_fp(params.m_fp),
    m_fp2(params.m_fp2),
    m_dad(params.m_dad)
  {
    std::cout << "item constructor called\n";
  }
  ~item() {}
  // Method invoked externally
  int callback() {
    std::cout << "item class method callback invoked\n";
    // In the real case here do general stuff
    if(m_fp) {
      int i = (m_dad->*m_fp)(1); // call member function through its pointer
      return i;
    } else {
      std::cout << "callback not registered\n";
      return 0;
    }
  }
  // Method invoked externally
  bool callback2() {
    std::cout << "items class method callback2 invoked\n";
    // In the real case here do general stuff
    if(m_fp2) {
      bool b = (m_dad->*m_fp2)(true); // call member function through its pointer
      return b;
    } else {
      std::cout << "callback2 not registered\n";
      return false;
    }
  }
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Methods that optionally add more functionalities to the 2 callbacks
  int  func1(int i);
  bool func2(bool b);
public:
  // Item member
  item<user> m_item;
public:
  user();
  ~user();
};

- - C ++ - - test.cpp

#include "test.h"

user::user() : m_item(init<user>(this).has_funcPtr(&user::func1) ) {
  std::cout << "user constructor called\n";
}

int user::func1(int i) {return i;}
bool user::func2(bool b) {return b;} // func2 won't be registered


int main() {
  user* u = new user();
  // Test callbacks
  int i = u->m_item.callback();
  bool b = u->m_item.callback2();
  std::cout << "main is printing i=" << i << " and b=" << b << "\n";
  std::cout << "expected results are i=1 and b=0\n" << "END\n";
  return 0;
}

ВЫВОД:

init constructor called
item constructor called
user constructor called
item class method callback invoked
items class method callback2 invoked
callback2 not registered
main is printing i=1 and b=0
expected results are i=1 and b=0
END
person Bertone    schedule 21.10.2015