首页 文章

C 11纯虚拟'templated'非模板化基类中的返回类型

提问于
浏览
0

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

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

我有两个类,AttributeBase和Attribute . AttributeSet使用AttributeBase,它只存储Attribute <T>的链接列表的起点和终点(作为AttributeBase *),并对列表进行修改 . AttributeBase是Attribute <T>的基类,仅为了制作通用指针而在设计中 . 当然,Attribute <T>是存储实际值的AttributeBase的特定类型 . 每个Attribute <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-> GetValue(),它会出错,因为GetBalue()没有在AttributeBase中定义,只有子类 . 你会认为编译器会知道指针必须指向派生类(唯一的派生类型),因为不能直接构造AttributeBase .

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

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

2 回答

  • 1

    因此,不存在真正通用的解决方案 . 您无法从基类获取任何类型,因为基类虚函数的所有覆盖都必须具有相同的返回类型 .

    这留给你两个选择 .

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

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

  • 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) ,但我更喜欢显式模板参数) .

相关问题