模板类覆盖基类虚函数

时间:2014-06-25 20:22:44

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

请考虑以下代码:

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? 谢谢!

2 个答案:

答案 0 :(得分:4)

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

您是slicing上面的IntSpecialization对象。为了使您的代码有效,GetSpecialization必须返回Base *Base&。例如,以下内容可按您的意图使用:

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

答案 1 :(得分:0)

只需让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; } ); }