理解通过获取非静态成员的地址来形成指向成员函数的指针的错误?

时间:2019-04-04 10:35:51

标签: c++ member-function-pointers

我有一个不确定的问题。

我正在使用一个库,该类的实例是通过传递对一些函数的引用来构造的。

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::*)()''void(*)()'的转换。

我尝试阅读有关此错误的类似问题,但似乎找不到一种既能理解又能在这种情况下提供帮助的解决方案。

我考虑过尝试向State添加一个重载构造函数,但是考虑到我可以从其他任何类调用它,这在每个类的情况下似乎是荒谬的。

我也有一段时间想过,我可以使函数静态化(因为对于MyClass的所有实例它们都是相同的),但是我很快想起它们将无法使用MyClass的非静态成员变量。

也许可以通过另一种方式提供转换或为State构造函数提供指针的方式吗?

我真的可以在这方面提供一些帮助,因为我对如何前进的想法非常执着。任何帮助或提示将不胜感激!

2 个答案:

答案 0 :(得分:2)

指向非静态成员函数的指针不能转换为普通函数指针。 void (MyClass::*)()需要一个MyClass对象才能对其进行操作。尽管原始指针无法保持任何状态,所以void(*)()无法存储有关要操作哪个MyClass对象的信息。

幸运的是,标准库具有一个专门用于此目的的类模板:std::functionstd::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(); })
  {}
};

Live Demo

在这里,不是将指针直接传递给成员函数,而是将它们包装在lambda中,以捕获要调用的对象的this指针。现在State对象可以调用这些成员,并且他们知道要对MyClass的哪个实例进行操作。

但是要注意对象的生命周期。由于state_a将指针保留回其MyClass实例,因此您需要确保MyClass的复制/移动构造函数和赋值运算符能够正确执行操作。编译器提供的默认实现还不够。

答案 1 :(得分:0)

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的构造函数以接受正确类型的指针。