【问题标题】:Template class override base class virtual function模板类覆盖基类虚函数
【发布时间】:2014-08-16 13:25:24
【问题描述】:

考虑以下代码:

class Base
{
public:
    virtual void* allocate(){ return nullptr; }
};

template <class T> class BaseTemplate : public Base
{
public:
    void* allocate() override { return new T(); }
};

class IntSpecialization : public BaseTemplate<int>
{
};

Base GetSpecialization(const int&){ return IntSpecialization(); }

目标是能够使用模板实现特化,但仍允许用户使用基类接口工作,例如:

int a;
auto s = GetSpecialization(a);
auto p = s.allocate();

上面的代码不起作用; s.allocate() 总是返回 nullptr,原因很明显。

我绝对需要GetSpecialization 函数来返回Base 非模板类,那么我该怎么做呢?

Base类的虚方法不能是纯的,否则会变成抽象的,会在GetSpecialization处编译失败。

解决这种模式的最佳方法是什么?使用 C++11? 谢谢!

【问题讨论】:

    标签: c++ templates c++11 overriding virtual


    【解决方案1】:
    Base GetSpecialization(const int&){ return IntSpecialization(); }
    

    您是上面的IntSpecialization 对象slicing。要使您的代码正常工作,GetSpecialization 必须返回 Base *Base&amp;。例如,以下内容将按您的预期工作:

    std::unique_ptr<Base> GetSpecialization(const int&)
    { 
        return std::unique_ptr<Base>(new IntSpecialization()); 
    }
    

    Live demo

    要使上述代码正常工作,您需要将 virtual 析构函数添加到 Base

    class Base
    {
    public:
        virtual void* allocate(){ return nullptr; }
        virtual ~Base() = default;
    };
    

    否则,当unique_ptr 超出范围时,它将调用delete ptr;,其中ptr 的类型为Base *,并且通过基类指针多态删除派生类对象是未定义的行为除非基类析构函数是virtual

    【讨论】:

    • 你也可以return {new IntSpecialization()}吗?
    • @0x499602D2 unique_ptr(T*) 构造函数是explicit,所以复制列表初始化会失败。
    • @T.C.因为他的 Base 没有 virtual 析构函数。我应该将其添加到答案中。
    • @Praetorian 好点,但是您的代码然后将其移动构造为std::unique_ptr&lt;Base&gt;...这不是违背目的吗?
    • @T.C.我只是在玩弄它,看看它是否真的有效。我假设因为我从unique_ptr&lt;Derived&gt; 构造unique_ptr&lt;Base&gt;,所以前者会复制/移动删除器并使用default_delete&lt;Derived&gt; 删除对象,但looks like 我错了。是时候再次更新答案了。
    【解决方案2】:

    只要让 Base 有指向 BaseTemplate 的指针:

    class BaseInterface {
    public:
        virtual void* allocate() = 0;
    }
    
    class Base
    {
       std::unique_ptr<BaseInterface> interface;
    public:
        Base( BaseInterface *i ) : interface( i ) {}
        void* allocate(){ return interface->allocate(); }
    };
    
    template <class T> class BaseTemplate : public BaseInterface
    {
    public:
        void* allocate() override { return new T(); }
    };
    
    class IntSpecialization : public BaseTemplate<int>
    {
    };
    
    Base GetSpecialization(const int&){ return Base( new IntSpecialization ); }
    

    不那么冗长的解决方案是使用std::function 和 lambda

    class Base 
    {
    public:
        typedef std::function<void *()> creator;
        Base( const creator &c ) : cr( c ) {}
        void *allocate() { return cr(); }
    private:
        creator cr;
    };
    
    template<class T>
    Base GetSpecialization( const T & ) { return Base( []() { return new T; } ); }
    

    【讨论】:

      猜你喜欢
      • 2017-06-28
      • 2021-11-08
      • 2014-03-08
      • 1970-01-01
      • 2017-09-22
      • 1970-01-01
      • 2018-05-18
      • 2014-02-06
      • 1970-01-01
      相关资源
      最近更新 更多