如何从GUIApplication单例实例获取模板类型实现非模板类锁操作
问题
我正在开发一款GUI应用,其中GUIApplication类是依赖模板化Mutex和模板化事件处理任务的单例类。现有其他非模板类(如Foo)需要在执行操作前锁定该单例的Mutex,但我不想将这些类也改为模板类,请问是否有办法从GUIApplication的当前实例中推导或保存其模板类型?
相关代码如下:
GUIApplication.hpp
#include <cassert> template<typename Task_T, typename Mutex_T> class GUIApplication { public: GUIApplication() { m_pInstance = this; } static Mutex_T& getMutexInstance() { assert(nullptr != m_pInstance); return m_pInstance->m_GuiMutex; } private: inline static GUIApplication<Task_T, Mutex_T>* m_pInstance = nullptr; Task_T m_eventTask; Mutex_T m_GuiMutex; };
Foo.hpp
class Foo { public: Foo(); };
Foo.cpp
#include <mutex> Foo::Foo() { //此处模板自动参数推导失败 auto lock = std::scoped_lock(GUIApplication::getMutexInstance()); //执行相关操作.... }
解决方案
以下是几种无需将Foo改为模板类的可行方案:
方案1:抽象基类+类型擦除
给GUIApplication模板类定义一个非模板基类,将Mutex的核心操作(lock/unlock)抽象为纯虚函数,这样非模板类可以通过基类接口访问Mutex,无需关心具体模板类型。
步骤1:定义非模板基类
#include <mutex> class GUIApplicationBase { public: virtual ~GUIApplicationBase() = default; virtual void lock() = 0; virtual void unlock() = 0; // 存储全局单例基类指针 static GUIApplicationBase*& getGlobalInstance() { static GUIApplicationBase* instance = nullptr; return instance; } };
步骤2:修改GUIApplication模板类
让模板类继承基类并实现纯虚函数,同时在构造时更新基类的全局指针:
#include <cassert> template<typename Task_T, typename Mutex_T> class GUIApplication : public GUIApplicationBase { public: GUIApplication() { getGlobalInstance() = this; m_pInstance = this; } static Mutex_T& getMutexInstance() { assert(nullptr != m_pInstance); return m_pInstance->m_GuiMutex; } // 实现基类的锁定接口 void lock() override { m_GuiMutex.lock(); } void unlock() override { m_GuiMutex.unlock(); } private: inline static GUIApplication<Task_T, Mutex_T>* m_pInstance = nullptr; Task_T m_eventTask; Mutex_T m_GuiMutex; };
步骤3:修改Foo.cpp的使用逻辑
为了让std::scoped_lock能直接配合基类使用,可以封装一个简单的锁适配器,或者直接手动调用lock/unlock:
#include <mutex> #include "GUIApplication.hpp" // 可选:适配基类的锁包装器,让std::scoped_lock可直接使用 struct GUIAdapterLock { GUIApplicationBase& base; GUIAdapterLock(GUIApplicationBase& b) : base(b) { base.lock(); } ~GUIAdapterLock() { base.unlock(); } }; Foo::Foo() { GUIApplicationBase* instance = GUIApplicationBase::getGlobalInstance(); assert(instance != nullptr); // 方式1:使用自定义适配器 GUIAdapterLock lock(*instance); // 方式2:手动锁定(不推荐,容易遗漏unlock) // instance->lock(); // try { // // 执行相关操作 // } finally { // instance->unlock(); // } }
这种方案类型安全、扩展性强,是最推荐的实现方式。
方案2:全局存储Mutex指针
在GUIApplication实例化时,将Mutex的指针存储到一个非模板全局变量中,非模板类通过类型转换获取Mutex引用。注意这种方式需要提前确定Mutex的具体类型,或者保证所有可能的Mutex类型接口兼容。
修改GUIApplication.hpp
#include <cassert> // 全局存储Mutex的指针 inline static void* g_guiMutex = nullptr; template<typename Task_T, typename Mutex_T> class GUIApplication { public: GUIApplication() { m_pInstance = this; g_guiMutex = &m_GuiMutex; } static Mutex_T& getMutexInstance() { assert(nullptr != m_pInstance); return m_pInstance->m_GuiMutex; } private: inline static GUIApplication<Task_T, Mutex_T>* m_pInstance = nullptr; Task_T m_eventTask; Mutex_T m_GuiMutex; };
修改Foo.cpp
#include <mutex> #include "GUIApplication.hpp" Foo::Foo() { assert(g_guiMutex != nullptr); // 假设实例化的Mutex类型是std::mutex,根据实际情况替换 auto& mutex = *static_cast<std::mutex*>(g_guiMutex); std::scoped_lock lock(mutex); // 执行相关操作 }
此方案实现简单,但类型安全性依赖于开发者对Mutex类型的准确认知,适合简单场景。
方案3:C++17+ std::any存储Mutex引用
利用C++17的std::any存储Mutex的引用,通过非模板接口获取,同样需要知道Mutex的具体类型。
修改GUIApplication.hpp
#include <cassert> #include <any> #include <functional> inline static std::any g_guiMutexHolder; template<typename Task_T, typename Mutex_T> class GUIApplication { public: GUIApplication() { m_pInstance = this; // 存储Mutex的引用包装 g_guiMutexHolder = std::ref(m_GuiMutex); } static Mutex_T& getMutexInstance() { assert(nullptr != m_pInstance); return m_pInstance->m_GuiMutex; } // 非模板的全局获取函数 template<typename MutexType> static MutexType& getGlobalMutex() { return std::any_cast<std::reference_wrapper<MutexType>>(g_guiMutexHolder).get(); } private: inline static GUIApplication<Task_T, Mutex_T>* m_pInstance = nullptr; Task_T m_eventTask; Mutex_T m_GuiMutex; };
修改Foo.cpp
#include <mutex> #include "GUIApplication.hpp" Foo::Foo() { // 指定Mutex的具体类型 auto& mutex = GUIApplication<int, std::mutex>::getGlobalMutex<std::mutex>(); std::scoped_lock lock(mutex); // 执行相关操作 }
此方案依赖C++17特性,适合现代项目,类型安全性比方案2高,但仍需明确Mutex类型。
内容的提问来源于stack exchange,提问作者JuliusCaesar
相关产品推荐
相关产品推荐

