0

这是我要完成的工作:

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

我有两个类,AttributeBase 和 Attribute。AttributeBase 由 AttributeSet 使用,它只存储 Attribute<T> 的链表(作为 AttributeBase* 的)的起点和终点,并对链表进行修改。AttributeBase 是 Attribute<T> 的基类,仅在设计中用于制作通用指针。当然,Attribute<T> 是存​​储实际值的特定类型的 AttributeBase。每个 Attribute<T> 的主要数据是一个继承的字符串(属性的名称,或者“键”,如果你愿意的话)和一个 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() 它会出错,因为 GetValue() 没有在 AttributeBase 中定义,只有子类。您会认为编译器会知道指针必须指向派生类(唯一的派生类型),因为 AttributeBase 不能直接构造。

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

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

4

2 回答 2

1

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

这给你两个选择。

首先,您可以提前决定要让您的列表包含派生自某个常见基本类型的任何对象。这将严重限制您可以放入列表中的内容,但至少一旦它们存在,您就可以完全自由地使用这些对象。

其次,根据您想要对列表中的对象实际执行的操作,您可以查看新的Boost.TypeErasure库。如果您需要对 list 做的就是,例如,将它们全部输出,或者一些少量的操作,这可以帮助您实现目标。

于 2013-10-31T21:36:51.293 回答
1

由于 和 的签名GetValue取决于SetValue类型,因此它们需要是模板。但它们可以是模板成员,而不需要类模板。

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可能会自动推断出它的模板参数,并且可能会错误地执行此操作。例如,如果a是一个AttributeBase&引用,它实际上是一个Attribute<long int>,则a.SetValue(1)与 相同a.SetValue<int>(1),这将抛出。正确的表达式是a.SetValue<long int>(1)(或a.SetValue(1L),但我更喜欢显式模板参数)。

于 2013-10-31T23:49:27.660 回答