【发布时间】:2020-07-20 09:36:43
【问题描述】:
问题:我有一个模板函数,它接受模板参数类的对象指针和指向该类方法的方法指针。然后我可以立即在该对象上调用该方法。但我不想想要立即调用它。相反,我想保存这两个指针以供将来使用,并在以后在不会根据上下文知道该类型是什么的代码中调用它们。
在旧版 C/C++99 代码中,我们将一个函数指针和一个 void* 用户数据指针传递给将执行回调的代码(例如,在计时器结束、用户事件等时)我们'd 几乎总是将对象指针作为用户数据传递,并编写一个单行 C 函数,将用户数据指针转换为该类型并调用对象上的方法:
void TimerCB( void* pvUserData, void* pvCallerData ) {
( (Foo*) pvUserData )->TimerDone( pvCallerData );
}
在 C++11 中,std::function 允许我们传入 lambda 和 std::bind,或者是没有用户数据的 C 函数。
然而,在实践中,几乎每次我都只想在当前对象上调用一个方法。我可以使用 lambda 或绑定来做到这一点,但它很冗长:
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 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 );
但是,这是不可接受的,因为我的 Timer 类属于项目的实用程序库级别,不需要了解每个可能的用户类,例如 Foo。
好的,事实证明我可以模板化该方法,因此我不再需要知道 Foo 是什么类型的对象或该方法是一个方法。这编译没有错误。 (方法和类指针交换了,所以很清楚调用了哪个重载函数。)
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 仍然运行,则会调用该回调。
【问题讨论】:
标签: c++11 callback function-templates pointer-to-member-functions