我正在尝试使用模板获取std:项目列表,其中每个项目都有一个指向包含它的列表的指针,但我一直在点击编译器消息。
这是一个非常精简的代码版本。
template <class E> class Item
{
public:
E* owner; // pointer to list that owns us.
};
template <class E> class BaseList: public std::list<E>
{
protected:
typedef std::list<E> inherited;
public:
void push_back(const E &e)
{
E tmp(e);
tmp.owner = this; // This line gives the error.
inherited::push_back(tmp);
}
};
class MyList;
class MyItem : public Item<MyList>
{
};
class MyList : public BaseList<MyItem>
{
};
void foo() // test code to instantiate template
{
MyList l;
MyItem m;
l.push_back(m);
}
然而,我的编译器barf在行: -
tmp.owner = this;
错误是:
[BCC32 Error] Unit7.cpp(30): E2034 Cannot convert 'BaseList<MyItem> * const' to 'MyList *'
就像“这个”在某种程度上变成了常量,但我看不出原因。编译器是Codegear C ++ Builder 2009。
我承认我不是100%使用模板,所以我不确定这是我的问题还是编译器。没有模板使用的相同代码编译得很好,但显然这不是我想要的,因为我有几个项目/列表类想要以这种方式工作。
另外,是否有更好的技术可以避免在每个项目中包含所有“所有者”指针?
编辑:我认为我将示例剥离得太过分了:“MyList”实际上引入了新方法,“MyItem”必须通过“owner”指针访问。
摘要:感谢所有评论和回答。正如公认的答案所说,问题只是指向BaseList与MyList之间的类型不兼容之一。
从STL容器和替代设计中提出的问题也很有帮助,但我使用的解决方案基本上与下面的Luc Touraille相同。
答案 0 :(得分:8)
在第30行,“this”是指向BaseList<MyIteM>
的指针,而不是MyList。您可以使用派生类替换类,但不能用其他方式替换。
您可以将myde MyList设置为BaseList<MyItem>
,如下所示:
typedef BaseList<MyItem> MyList
或者让MyItem来自Item<BaseList<MyItem> >
。
从类型派生时,您可以创建不同的类型。当您键入def时,您将为该类型创建别名。因此,当你输入typedef时,编译器会接受这个。
答案 1 :(得分:4)
除了您已有的答案之外,我还要指出标准库集合类并非旨在派生,因为它们没有虚拟析构函数,并且它们的成员函数都不是虚拟的。
答案 2 :(得分:3)
不应该是tmp.owner = static_cast<MyList*>(this)
。 E的类型是MyItem中的MyList,因此E *将是MyList *。此指针的类型将是BaseList *,因此编译器会给出错误,即您无法将基类指针转换为派生类指针。
答案 3 :(得分:2)
当你没有说出需要时,很难说是否有更好的解决方案。 为什么每个元素都需要指向它们存储在列表中的指针?
无论如何,当您从标准容器继承时,可能会发生不好的事情。他们没有虚拟析构函数,所以你必须非常小心。
更好的解决方案可能是提供执行push_back的免费功能:
template <typename T>
void push_back(std::list<T>& list, const T& t) {
T tmp(t);
tmp.owner = this;
list.push_back(tmp);
}
除了避免非虚拟析构函数问题之外,它还解决了编译器错误,因为您现在只有一种类型的列表。
当然,如果我们首先知道你为什么需要这个所有者指针,那么可能存在更好的解决方案。
修改强>
在回复您的编辑和评论时,请使用合成,而不是继承:
struct House {
std::string zipcode;
std::list<Person> persons;
void AddPerson(const Person& person) {
Person tmp(person);
tmp.owner = this; // The owner field should be a house, not the list of persons.
persons.push_back(tmp);
}
};
虽然我没有卖出几乎循环的引用,但是当一个House存储一个人员列表时,你会得到一个人,并且一个人有一个指向它所在的House的指针。
我更愿意尽可能地分离这些类。如果我想给某人发信,我会打电话给SendLetter(人,房)。在那所房子里给这个人发一封信。
答案 4 :(得分:1)
在旁注中,你不应该从std扩展任何类,它们不是为它构建的。
具体来说,它们没有虚析构函数,因此当您在指向基类的指针上调用delete时,派生类的析构函数将永远不会被调用。
您可以阅读更多内容Advice on a better way to extend C++ STL container with user-defined methods
答案 5 :(得分:1)
我喜欢jalf的自由功能理念。我做到了:
template <class X, class Y> // X must have a push_back(Y) member, Y must have an X* owner member
void push_back(X& container, Y value)
{
value.owner = container;
container.push_back(value);
}
这与X传递的是否
无关答案 6 :(得分:1)
正如已经指出的那样,影响
tmp.owner = this;
失败,因为this
与tmp.owner
的类型不同。一种解决方案是执行强制转换,但为此,您需要将容器类型提供给BaseList。这可以使用Item中的typedef来完成。这是代码:
template <class Item> class BaseList
{
public:
void push_back(Item i)
{
i.owner = static_cast<Item::containerType *>(this); // note the cast
items.push_back(i);
}
Item & back() { return items.back(); }
protected:
std::list<Item> items;
};
template <class Container> class Item
{
public:
typedef Container containerType; // Typedef used by BaseList
containerType* owner; // pointer to list that owns us.
};
我还删除了std::list
的公开派生:正如许多人所说,这是(大部分时间)最好避免的;你应该考虑使用组合,或者私人继承。
P.S。:我尝试让owner
私人和BaseList<Item>::push_back
朋友成为Item,但我没有成功。有可能吗? (如果在评论中回答的时间太长,请随时提出问题并回答)
答案 7 :(得分:1)
关于const
:编译器提到的类型BaseList<MyItem> * const
是一个红色的鲱鱼 - 它不是指向一个指针 - const
- 对象,但是是 const
的指针,即一个不会改变的地址。 (当你想到它时,this
永远不会改变指向别的东西,是吗?)