我想做的是:
class Ba { }
class Da : public Ba {}
class Db : public Ba {}
class Bb // abstract base class that must not be a template.
{
void Process()
{
list<Ba*>::iterator pos;
// I known the derived class will also derive
// from list<Dx*> where Dx derives from Ba
for(pos = this->begin(); pos < this->end(); pos++)
something(*pos);
}
}
template<T> class L : public Bb , list<T*> // T is always derived from Ba
{
}
但那是无效的。 那么下一个最好的东西是什么?
一个交替的公式有一个全局Process
得到一个Bb
指针,其中的类型不知道直到运行时:
void GlobalProcess(Bb* bb) // don't know what kind of Bb (which L) got passed in.
{
list<Ba*>::iterator pos;
// I known the derived class will also derive
// from list<Dx*> where Dx derives from Ba
for(pos = bb->begin(); pos < bb->end(); pos++)
something(*pos);
}
我有几个理由这样做,但大约一半的原因是感觉不到C ++系统的边缘。
我能提出的最简单的问题形式是我需要从代码中迭代list<D*>
,其中我在编译时允许使用的所有信息都是D
派生的来自B
。这种情况可能来自于通过指向抽象基类的指针传递list<D>
或者其自身在基类中的代码。
如果仅list<D*>::iterator
list<B*>::iterator
,那么这一切都会正常工作
答案 0 :(得分:8)
首先,不要从std::list<>
继承,STL中的类(有一些小的例外)并不意味着派生自。他们没有虚拟成员,这样做没有任何好处,只会导致问题。
你的问题有点含糊不清。但假设你想这样做:
struct A {};
struct B : public A {};
std::list<B> l;
for(std::list<A>::iterator it = l.begin(); it != l.end(); ++it) {
whatever(*it);
}
显然这是无效的。但你可以这样做:
struct A {};
struct B : public A {};
std::list<B> l;
void whatever(A& a) {
// do something with an A
}
std::list<B> l;
for(std::list<B>::iterator it = l.begin(); it != l.end(); ++it) {
whatever(*it);
}
或更好:
struct A {};
struct B : public A {};
std::list<B> l;
void whatever(A& a) {
// do something with an A
}
std::for_each(l.begin(), l.end(), whatever);
修改强>
要做你想做的事,你只需要做一些额外的模板工作:
class Ba {};
class Da : public Ba {};
class Db : public Ba {};
template <class T>
class Bb {
void Process() {
list<T>::iterator pos;
for(pos = begin(); pos < end(); pos++)
something(*pos);
}
}
template <class T>
class L : public Bb<T>, std::list<T> {
}
或您可以像这样制作流程和界面:
template <class T>
class Bb {
public:
virtual void Process() = 0;
}
template <class T>
class L : public Bb<T>, std::list<T> {
public:
virtual void Process() {
list<T>::iterator pos;
for(pos = begin(); pos < end(); pos++)
something(*pos);
}
}
但最好不要继承std::list<>
远,因为它几乎肯定会导致错误。最好让std::list<T>
成为L
类的成员。
答案 1 :(得分:3)
在C ++中获得你想要的东西基本上是不可能的。该语言不起作用,X : Y
并不意味着C<X> : C<Y>
实际上是您要应用的规则。为什么不呢?有几个原因:
C<X>
可能与C<Y>
完全不同。)你打开自己的问题(伪代码,因为这个问题不是C ++独有的)
class Base
{
void method();
};
class Child : public Base
{
void newmethod();
};
list<Base> base_list;
list<Child> child_list;
void safe(list<Base> list)
{
for(it in list)
{ it->method(); }
}
void still_safe(list<Child> list)
{
for(it in list)
{ it->newmethod(); }
}
safe(base_list);
safe(child_list);
still_safe(child_list);
void unsafe(list<Base> list)
{
list.push_back(new Base);
}
unsafe(base_list); // safe
safe(base_list);
unsafe(child_list); // maybe we get away with this
still_safe(child_list); // KA BOOM! because the last item in child_list can't newmethod()
请注意,您可以在某些语言中执行更有限的形式(以某种方式工作且安全),但我无法想到任何能够明确满足您要求的内容。另请参阅http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
在C ++中,对于这类问题,真正的解决方案是利用多态性(无论是通过列表/迭代器类型参数化的模板还是在C和D上运行的虚拟方法)。
答案 2 :(得分:0)
这可以通过将处理代码移动到模板化类中来实现:
class Bb
{
virtual void Process() = 0;
}
template<T> class L : public Bb , list<T> // T is always derived from Ba
{
virtual void Process()
{
list<T>::iterator pos;
for(pos = begin(); pos < end(); pos++)
something(*pos);
}
}
这样您就可以在Process()
中指定正确的迭代器类型,并仍然可以从通用*Bb
或&Bb
中调用它。
如果您真的不想使用虚拟功能并希望在课程Bb
中完成所有工作,那么Bb
需要了解它应该访问的list<T>
。所以它以某种方式需要知道类型T
,它不再是不同类型T
的通用。
如果你有几种不同的类型T
,你需要在某处使用多态,这意味着要使用一些虚函数或模板。
答案 3 :(得分:0)
如果您的迭代代码在基类中,那么您就是列表 迭代,也必须在基类中。这导致了 问题,列表应该包含什么类型的对象,因为你想要 确定派生类中的类型。要解决这个问题,你可以 存储共享指针,有效地具有可以存储的列表 多态类型。这是代码:
#include <list>
#include <boost/shared_ptr.hpp>
class Ba { };
typedef boost::shared_ptr<Ba> BaSP;
class Da : public Ba {};
class Db : public Ba {};
void something(BaSP& a) {
// do something with an Ba
}
class Bb // abstract base class that must not be a template.
{
protected:
std::list<BaSP> list_;
void Process()
{
std::list<BaSP>::iterator pos;
for(pos = list_.begin(); pos != list_.end(); pos++)
something(*pos);
}
};
class L : public Bb
{
L() {
// the list can store Da and Db objects
boost::shared_ptr<Da> da;
list_.push_back(da);
boost::shared_ptr<Db> db;
list_.push_back(db);
}
};
这是您发布的代码的下一个最佳选择。如果你给我们了 你想要实现的目标更大,我们可能会出现 设计更好。