为啥我不能返回对使用模板创建的派生类的 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&lt;IPlugin&gt;,并且效果很好。但是现在我已经将返回类型切换为IPlugin &amp;,当我在结果上调用虚函数时,会调用基类函数,而不是派生函数。

【问题讨论】:

创建minimal reproducible example IPluginIRenderer有关系吗? 不完整的、无法工作的代码太……嗯……不完整,任何人都无法提供帮助。什么是IPlugin,它与模板化PluginRegistrar 专门/实例化的T 有什么关系?使用模板化类/函数的代码呢? 对不起,我认为这比需要完整代码更明显。现在应该是那里的一切。 我认为auto plugin= factory.GetPlugin("TestPlugin"); 应该是auto&amp; 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 的引用?的主要内容,如果未能解决你的问题,请参考以下文章

覆盖派生模板类的返回类型

为啥派生类的朋友不能使用受保护的成员?

为啥我不能同时实例化对基类的引用作为派生类的指针?

为啥当从派生类访问基类的数据时它会返回废话(我认为是指针数据)

传递派生模板类的向量

为啥不能从派生类对象访问父类的赋值运算符