为啥我不能返回对使用模板创建的派生类的 unique_ptr 的引用?
Posted
技术标签:
【中文标题】为啥我不能返回对使用模板创建的派生类的 unique_ptr 的引用?【英文标题】:Why can't I return a reference to a unique_ptr of a derived class created with a template?为什么我不能返回对使用模板创建的派生类的 unique_ptr 的引用? 【发布时间】:2021-02-22 12:54:59 【问题描述】:我有一个基于 this link 的编译时“插件”系统,我想保留插件的所有权,这些插件实际上是独一无二的 - 每种类型应该只有一个实例。
有一个注册器类,它拥有一个插件类型,并拥有该插件的 unique_ptr。
我想在请求时返回对插件的引用,以保持所有权清晰。
更完整的代码如下。请注意插件标头中用于注册的#define。
另外,我在这里将代码中的 Renderer 重命名为 Plugin,所以如果我错过了任何随机 Render 引用的内容。
工厂类:
class PluginFactory
public:
// Instance of Factory
static PluginFactory& Instance();
void Register(IPluginRegistrar *Registrar, std::string name);
IPlugin& GetPlugin(std::string name);
private:
std::unordered_map<std::string, IPluginRegistrar *>_registry;
// private constructor, prevent copying
PluginFactory() : _registry() ;
PluginFactory(PluginFactoryconst&) = delete;
void operator=(PluginFactoryconst&) = delete;
;
PluginFactory& PluginFactory::Instance()
static PluginFactoryinstance;
return instance;
void PluginFactory::Register(IPluginRegistrar *registrar, std::string name)
if (_registry.count(name))
std::cout << name << " is already registered!\n";
else
std::cout << "Registering " << name << "\n";
_registry[name] = registrar;
IPlugin& PluginFactory::GetPlugin(std::string name)
IPluginRegistrar* registrar = _registry.at(name);
return registrar->GetPlugin();
#define REGISTER_PLUGIN(CLASSNAME) \
namespace \
static MyNamespace::PluginRegistrar<CLASSNAME>\
_registrar( #CLASSNAME ); \
;
插件库
class IPlugin
public:
IPlugin() std::cout << "Constructing IPlugin class\n";
virtual void DoThing() std::cout << "Here's the thing from an IPlugin!\n"; ;
;
插件派生
class TestPlugin : public IPlugin
public:
TestPlugin ();
virtual void DoThing() override;
;
REGISTER_RENDERER(TestPlugin)
TestPlugin::TestPlugin () : IPlugin()
std::cout << "Constructing TestPlugin class\n";
void TestPlugin::DoThing()
std::cout << "Here's the thing from a TestPlugin!\n";
注册商
class IPluginRegistrar
public:
virtual IPlugin& GetPlugin() = 0;
;
template <class T>
class PluginRegistrar: public IPluginRegistrar
public:
PluginRegistrar(std::string classname); // constructor registers plugin with a plugin factory
IPlugin& GetPlugin(); // GetPlugin is called by the factory
private:
std::string _name;
std::unique_ptr<IPlugin> _ptr;
;
template <class T>
PluginRegistrar<T>::PluginRegistrar(std::string classname) : _name(classname)
PluginFactory& factory = PluginFactory::Instance();
factory.Register(this, classname);
template <class T>
IPlugin &
PluginRegistrar<T>::GetPlugin()
if (!_ptr)
_ptr = std::make_unique<T>();
return *_ptr;
使用代码:
auto& factory = PluginFactory::Instance();
auto plugin= factory.GetPlugin("TestPlugin");
=> outputs confirm derived class constructed
plugin.DoThing();
=> output of IPlugin.DoThing(), not TestPlugin.DoThing()
每次调用GetPlugin()
时,此代码的原始版本都会返回一个新的unique_ptr<IPlugin>
,并且效果很好。但是现在我已经将返回类型切换为IPlugin &
,当我在结果上调用虚函数时,会调用基类函数,而不是派生函数。
【问题讨论】:
创建minimal reproducible exampleIPlugin
与IRenderer
有关系吗?
不完整的、无法工作的代码太……嗯……不完整,任何人都无法提供帮助。什么是IPlugin
,它与模板化PluginRegistrar
专门/实例化的T
有什么关系?使用模板化类/函数的代码呢?
对不起,我认为这比需要完整代码更明显。现在应该是那里的一切。
我认为auto plugin= factory.GetPlugin("TestPlugin");
应该是auto& plugin= factory.GetPlugin("TestPlugin");
(作为参考)
【参考方案1】:
auto plugin= factory.GetPlugin("TestPlugin");
这会将结果复制到一个新对象中,该对象将为IPlugin
(原始派生类型将为sliced)。您想改用引用:
auto& plugin= factory.GetPlugin("TestPlugin");
您可能希望将IPlugin::DoThing()
设为纯虚函数(即virtual void DoThing() = 0
),这将
-
强制派生类实现它
防止您拥有
IPlugin
类型的对象,即使您的原始代码无法编译
【讨论】:
以上是关于为啥我不能返回对使用模板创建的派生类的 unique_ptr 的引用?的主要内容,如果未能解决你的问题,请参考以下文章