Понимание ошибки при получении адреса нестатического члена для формирования указателя на функцию-член? - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть проблема, я не знаю, как ее обойти.

Я использую библиотеку, экземпляры класса которой создаются путем передачи ссылок на несколько функций.

void func_on_enter() {...}
void func() {...}
void func_on_exit(){...}

State state_a(&func_on_enter, &func, &func_on_exit);

Я пишу класс, который содержит экземпляр этого класса, и поэтому попытался сделать что-то вроде этого:

class MyClass{
private:
  void func_on_enter() {...}
  void func() {...}
  void func_on_exit(){...}

  State _state_a;

public:
  MyClass() : _state_a(&func_on_enter, &func, &func_on_exit) {}
};

Я получаю ошибку "ISO C ++ запрещает принимать адрес неквалифицированного илиНестатическая функция-член в скобках, чтобы сформировать указатель на функцию-член. Скажите '& MyClass :: _ func_on_enter' "(и то же самое для двух других методов, очевидно).Компилятор услужливо предоставляет решение, которое я попробовал.

Однако затем компилятор напоминает мне, что не существует соответствующей функции для вызова 'State::State(void (MyClass::*)(), void (MyClass::*)(), (MyClass::*)())' и что он не имеет известного преобразования из 'void (MyClass::*)()' в * 1013.*.

Я пытался прочитать похожие вопросы об этой ошибке, но, похоже, не могу найти решение, которое я и понимаю, и могу помочь в этой ситуации.

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

Некоторое время я даже думал, что могу просто сделать функции статичными.(поскольку они одинаковы для всех экземпляров MyClass), но я быстро вспомнил, что тогда они не смогут использовать нестатические переменные-члены MyClass.

Возможно, есть способ, которым я могу предоставитьпреобразование или предоставить указатели для конструктора State по-другому?

Я мог бы действительно помочь с этим, так как яЗастрял в идеях о том, как двигаться вперед.Любая помощь или советы будут с благодарностью!

Ответы [ 2 ]

2 голосов
/ 04 апреля 2019

Указатели на нестатические функции-члены не могут быть преобразованы в простые указатели функций. Для void (MyClass::*)() требуется объект MyClass для работы. Необработанные указатели не могут содержать какие-либо состояния, поэтому void(*)() не может хранить информацию о том, с каким MyClass объектом работать.

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

class State {
private:
  std::function<void()> on_enter;
  std::function<void()> func;
  std::function<void()> on_exit;

public:
  State(std::function<void()> on_enter, std::function<void()> func, std::function<void()> on_exit)
    : on_enter{std::move(on_enter)},
      func{std::move(func)},
      on_exit{std::move(on_exit)}
  {}

  void do_something() {
    on_enter();
    // stuff
    func();
    // more stuff
    on_exit();
  }
};

class MyClass {
private:
  void func_on_enter() {}
  void func() {}
  void func_on_exit(){}

  State state_a;

public:
  MyClass()
    : state_a([this]() { func_on_enter(); },
              [this]() { func(); },
              [this]() { func_on_exit(); })
  {}
};

Демоверсия

Здесь, вместо того, чтобы передавать указатели непосредственно на ваши функции-члены, я заключил их в лямбды, которые захватывают указатель this объекта, который нужно вызвать обратно. Теперь объект State может вызывать этих членов, и они знают, с каким экземпляром MyClass можно работать.

Будьте осторожны с продолжительностью жизни объекта. Так как state_a хранит указатели на свой экземпляр MyClass, вам нужно убедиться, что конструктор копирования / перемещения и операторы присваивания MyClass делают правильные вещи. Реализации по умолчанию, предоставляемые компилятором, недостаточны.

0 голосов
/ 04 апреля 2019
void func_on_enter() {...}
void func() {...}
void func_on_exit(){...}

State state_a(&func_on_enter, &func, &func_on_exit);

Чтобы это работало, конструктор State должен принимать указатели функций типа void(*)().

_state_a(&MyClass::func_on_enter, &MyClass::func, &MyClass::func_on_exit)

Чтобы это работало, конструктор State должен принимать указатели на функции-члены типа void (MyClass::*)().Указатели на функции-члены не могут быть преобразованы в указатели на функции.

Вы можете изменить конструктор State, чтобы принимать указатели правильного типа.

...