C ++ 11非模板化基类中的纯虚拟“模板化”返回类型

时间:2013-10-31 20:56:55

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

这就是我想要完成的事情:

我正在尝试创建各种类型的链接列表。为了实现这一点,我认为多态性将是一个很好的方式。

我有两个类,AttributeBase和Attribute。 AttributeSet使用AttributeBase,它只存储Attribute< T>的链接列表的起点和终点(作为AttributeBase *),并对列表进行修改。 AttributeBase是Attribute< T>的基类。这只是为了制作通用指针而在设计中。当然,属性< T>是存储实际值的AttributeBase的特定类型。每个属性的主要数据< T>是一个继承的字符串(属性的名称,或者是'key',如果你愿意的话)和一个类型为T的值。

所以,到目前为止,我已经(简化):

class AttributeBase
{
  public:
    AttributeBase() = delete;
    AttributeBase* GetNext() { return next; };
    AttributeBase* GetPrev() { return prev; };
    std::string GetName() { return name; };
    //Sometimes I need to get/set the value stored in a derived class
    //But, how would I define the function here since the return
    //type is of type T as defined in Attribute?
    virtual ???? GetValue = 0;
    virtual void SetValue(????) = 0;

    friend class AttributeSet;
  private:
    AttributeBase* next = nullptr;
    AttributeBase* prev = nullptr;
    std::string name;
};

template <class T>
class Attribute : public AttributeBase
{
  public:
    Attribute( std::string _name, T _value ){ name = _name; value = _value };
    T GetValue(){ return value; };
    void Setvalue(T){ value = T; };
  private:
    T value;
};

class AttributeSet
{
  public:
    template <class T>
    void Add(std::string,T); //Add an Attribute<T>(std::string,T) to the list
    void Delete(std::string);
    bool Contains(std::string _name); //Scan the list to determine if an
                                      //attribute with name of _name exists
    template <class T>
    T Get(std::string); //Scan the list for 'name' and return
                        //AttributeBase*->GetValue()
  private:
    AttributeBase* start = nullptr;
    AttributeBase* end = nullptr;
}

由于我试图保持AttributeBase泛型和非模板化(以避免AttributeSet中强类型的开始和结束指针),这会带来一个问题。如何为虚函数BaseAttribute :: GetValue()指定尚未指定的返回类型。我首先尝试使用auto,遇到编译错误。

由于没有实际创建AttributeBase的实例(并删除了默认构造函数),我认为可以省略GetValue并在派生类中定义它。但是,如果我尝试* AttributeBase-&gt; GetValue(),它会出错,因为GetBalue()没有在AttributeBase中定义,只有子类。你会认为编译器会知道指针必须指向派生类(唯一的派生类型),因为不能直接构造AttributeBase。

因此,为了使用GetValue(),我必须提前知道前一个值的类型,以便能够将AttributeBase *转换为Attribute *。如果AttributeBase本身是模板化的并且包含值T类型,那么这将是微不足道的。然后我可以访问AttributeBase * - &gt;类型来确定我需要转换的指针类型。但是,正如我所说,模板化AttributeBase会破坏对象的预期用途。

我很可能会以一种完全错误的方式(再一次)来解决这个问题。但在这一点上,我被困在想法。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

因此,一个真正普遍的解决方案并不存在。您只能从基类中获取任意类型,因为基类虚函数的所有覆盖都必须具有相同的返回类型。

这给你留下了两个选择。

首先,您可以提前决定您的列表是否包含任何源自某种常见基类型的对象。这将严重限制您可以放入列表中的内容,但至少您可以完全自由地使用这些对象。

其次,根据您对列表中的对象实际执行的操作,您可以查看新的Boost.TypeErasure库。如果您需要对列表进行所有操作,比如输出所有内容,或者进行少量操作,这可以帮助您实现目标。

答案 1 :(得分:1)

由于GetValueSetValue的签名取决于类型,因此它们必须是模板。但它们可以是模板成员而无需类模板。

class AttributeBase
{
  public:
    template <typename T> T GetValue() const;
    template <typename T> void SetValue(T);
//...
};

template <typename T>
T AttributeBase::GetValue() const
{
    return dynamic_cast<Attribute<T>&>(*this).GetValue();
}

template <typename T>
void AttributeBase::SetValue(T val)
{
    dynamic_cast<Attribute<T>&>(*this).SetValue(val);
}

template <typename T>
T AttributeSet::Get(std::string const& name) const
{
    // (assuming a private helper method Find().)
    const AttributeBase* attr = Find(name);
    if ( !attr )
        throw std::invalid_argument("attribute not in set");
    return attr->GetValue<T>();
}
但是,有一个问题:如果您碰巧使用了错误的类型,这些函数都将抛出异常。并且SetValue可能会自动推断出其模板参数,并且可能会错误地进行推断。例如,如果aAttributeBase&引用,实际上是Attribute<long int>,则a.SetValue(1)a.SetValue<int>(1)相同,后者将抛出。正确的表达式是a.SetValue<long int>(1)(或a.SetValue(1L),但我更喜欢显式模板参数。)