C ++ 11及之后:如何存储在模板化方法中接收的对象指针和方法指针以供以后使用?

C++11 and after: How to store an object pointer and method pointer, received in a templatized method, for later use?

问题:我有一个模板函数,它接受模板参数 class 的对象指针和指向 class 的方法的方法指针。然后我可以立即对该对象调用该方法。但我不想立即调用它。相反,我想保存这两个指针以供将来使用,并在以后的代码中调用它们,而这些代码不会根据上下文知道该类型是什么。

在遗留 C/C++99 代码中,我们将一个函数指针和一个 void* 用户数据指针传递给将执行回调的代码(例如,在计时器完成时,用户事件等)我们几乎总是传入一个对象指针作为用户数据,并编写一个单行 C 函数,将用户数据指针转换为该类型并调用该对象的方法:

void TimerCB( void* pvUserData, void* pvCallerData ) {
    ( (Foo*) pvUserData )->TimerDone( pvCallerData );
}

在 C++11 中,std::function 让我们可以传入 lambda 和 std::bind,或者一个没有用户数据的 C 函数。

然而,在实践中,几乎每次我都只想调用当前对象的一个​​方法。我可以用 lambda 或 bind 做到这一点,但它很冗长:

  class Timer {

      :

      virtual void SubscribeTimer( const char* pszTime,
                                   std::function<void(Data*)> pfn );
  };

  void Timer::SubscribeTimer( const char* pszTime,
                             std::function<void(Data*)> pfn ) {
      cout << "  calling std::function\n";
      Data d;
      pfn( &d );
  }

  // Inside methods of class Foo:

  SubscribeTimer( "14:59:50", std::bind( &Foo::TimerDone, this, std::placeholders::_1 ) );

  SubscribeTimer( "14:59:50", [this](Data* pdata){this->TimerDone( pdata );} );

我可以传入方法指针,如果我在编译时知道他们对象的class,就像这样:

  class Timer {

      :

      virtual void SubscribeTimer( const char* pszTime,
                                   void (Foo::*pfn)( Data* pd ), Foo* pfoo );
  };

  void Timer::SubscribeTimer( const char* pszTime, void (Foo::*pfn)( Data* pd ), Foo* pfoo ) {
      cout << "  calling method\n";
      Data d;
      (pfoo->*pfn)( &d );
 }

  // Inside methods of class Foo:

  SubscribeTimer( "14:59:50", &Foo::TimerDone, this );

但是,这是不可接受的,因为我的计时器 class 是项目的实用程序库级别,不需要像 Foo 这样的每个可能的用户 class 都知道.

好的,事实证明我可以将该方法模板化,因此我不再需要知道 Foo 是什么类型的对象或该方法是什么方法。这编译没有错误。 (方法和 class 指针交换,因此很清楚调用了哪个重载函数。)

  class Timer {

      :

      template<typename T> void SubscribeTimer( const char* pszTime, T* pthis,
                                                void (T::*pfn)( Data* pd ) );
  };



template<typename T> void Foo::SubscribeTimer( const char* pszTime, T* pthis,
                                                void (T::*pmethod)( Data* pd ) ) {
  cout << "  calling any method\n";
  Data d;
  (pthis->*pmethod)( &d ); // <-- PROBLEMATIC LINE
}

// Inside methods of class Foo:

SubscribeTimer( "14:59:50", this, &Foo::TimerDone );

所以... 胜利!这是我想要的更简单的语法,而不是上面显示的更混乱的 lambda 和 std::bind

但这是我的问题。 上面的示例之所以有效,是因为标记为 PROBLEMATIC LINE 的行位于编译器知道 pthis 类型的上下文中。但实际上,SubscribeTimer() 不会立即调用该回调。相反,它会保存该值以供将来参考。很久以后,如果应用程序仍然 运行 在 14:59:50,将调用该回调。

你已经知道答案的所有部分(std::function,lambdas);你只需要把它们放在一起。

std::function<void(Data*)> f = [=](Data* d) { (pthis->*pmethod)(d); }

现在保存这个功能,例如在数据成员中。到调用的时候,就是

Data d;
f_member(&d);