这就是我想要完成的事情:
我正在尝试创建各种类型的链接列表。为了实现这一点,我认为多态性将是一个很好的方式。
我有两个类,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会破坏对象的预期用途。
我很可能会以一种完全错误的方式(再一次)来解决这个问题。但在这一点上,我被困在想法。任何帮助将不胜感激!
答案 0 :(得分:1)
因此,一个真正普遍的解决方案并不存在。您只能从基类中获取任意类型,因为基类虚函数的所有覆盖都必须具有相同的返回类型。
这给你留下了两个选择。
首先,您可以提前决定您的列表是否包含任何源自某种常见基类型的对象。这将严重限制您可以放入列表中的内容,但至少您可以完全自由地使用这些对象。
其次,根据您对列表中的对象实际执行的操作,您可以查看新的Boost.TypeErasure库。如果您需要对列表进行所有操作,比如输出所有内容,或者进行少量操作,这可以帮助您实现目标。
答案 1 :(得分: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)
,但我更喜欢显式模板参数。)