Stackoverflow中提出了這樣一個問題:假設我們實現了一個User類,Library類,現在Library類中utility需要回調User中func方法,總結答案,常見的幾種方法如下:
- 靜態方法:將User類中func方法實現為靜態方法,Library類中進行調用。
- 虛擬方法:將User類繼承至基類Base,Base類中func方法為純虛函數。
- 直接回調:在Library中保存User類指針,utility方法中直接調用。
下面則是非常規方法。
基於tag dispatch的調用方法,利用空類實現標簽回調,在注冊方法時提供不同的空類模板參數實現不同的調用,代碼如下:
template<class T> struct tag_t { using type = T; constexpr tag_t() {} }; struct ForLibrary; template <class T> class Library { public: T *node = nullptr; Library(T *n):node(n) {} void utility() { func(tag_t<ForLibrary>(),node); }; }; class User { public: void func() { cout << "User::func" << endl; } friend void func(tag_t<ForLibrary>, User *self) { self->func(); } };
不得不說,這種方法太妙了,既可以動態變化回調方法(只需修改ForLibrary參數)又無需改變User類實現,而且一切都是靜態調用無虛函數開銷。
利用模板基類實現靜態多態機制,通過在基類中強轉類型調用子類方法,實現路由回調,代碼如下:
#include <iostream> #include <functional> template<typename T> class ICallback { public: void callback() { static_cast<T*>(this)->func(); } }; class User : public ICallback<User> { public: void func(){ std::cout << "User::func" << std::endl; } }; template<class T,class F=std::function<void(void)>> class Library { private: T *node; public: Library(T* n):node(n) { } void utility() { std::mem_fn(&T::callback)(*node); } void utility(F func) { func(); } }; int main() { User user; Library<User> lib(&user); lib.utility(); lib.utility(std::bind(&User::callback,user)); return 0; }
回調類與實現類相互解耦又能避免虛函數多態。