继承和模板参数

时间:2012-11-08 11:46:35

标签: c++ templates inheritance parameters

我有一个关于模板参数的相当简单的问题。我正在编写一个类Scheduler,它使用如下列表:

list<PartitionT<CompareJobReady, CompareJobRunning> > partitions;

PartitionT是一个使用priority_queues的模板化类,我想使用比较器类对这些队列进行参数化。 CompareJobReady和CompareJobRunning是这些类(它们实现了一个特定的operator())。

无论如何,由于PartitionT是一个模板化的类,我希望能够传递任何类型的比较器类。特别是,我已经定义了两个额外的类,即CompareJobReadyEDFVD(继承自CompareJobReady)和CompareJobRunningEDFVD(继承自CompareJobRunning)。

我现在真正想做的是能够写出这样的东西:

list<PartitionT<CompareJobReady, CompareJobRunning> > *p = new list<PartitionT<CompareJobReadyEDFVD, CompareJobRunningEDFVD> >();

但编译器告诉我转换是不可能的。什么是我的问题的最佳解决方案?

2 个答案:

答案 0 :(得分:1)

标准库的容器类拥有其元素。也就是说,如果你有:

class Base {};
class Child : public Base {};

std::list < Base > myList;
std::list < Base > *pMyList;

然后myList的元素只能作为(引用)类型为Base的对象来访问。您可以存储Base类型的元素(例如push_backemplace_back)并获取(例如front或通过迭代器)的引用/副本,例如,请参阅{{ 3}}。我们来看看push_back

void push_back(const value_type&);

value_type是您传递给std::list的第一个模板参数。在您的情况下,即PartitionT < CompareJobReady, CompareJobRunning >,或在上面的示例中,它是Basepush_back实际上复制您传递的参数,并使该副本成为新元素。这是为什么?因为新元素可以由列表拥有。当列表被自身销毁时,列表可以销毁此元素,如果移动/交换两个列表,则将其传递到另一个列表。如果元素没有被复制并从外部被销毁,那么列表将包含一个被破坏的元素 - 这将破坏列表给出的保证(并且它不会很好)。

另一个例子(简化,不太准确):list通过分配器为其元素分配内存。这里的默认分配器是std::allocator<Base>。它只为元素分配尽可能多的内存,以存储类型为Base的对象。

当您执行pMyList = new std::list < Child >;之类的操作时,右侧会生成“指向std::list<Child>的指针”,而左侧则是“指向std::list<Base>的指针”。这两种类型无关,因为std::list<Base> 不会继承或定义到std::list<Child> 的转换。有一些原因可以解释为什么通用编程需要知道它处理的类型。多态性是一种抽象,因此您不必 - 也不能 - 在编译时知道您正在处理的类型。

C ++中的多态性通过指针或引用来工作。因此,如果您想在列表中排列一些与此类型无关的Child个对象(例如,它只知道Base类型),则必须将它们存储为指针:

std::list < std::shared_ptr<Base> > myList2;

myList2.push_back( new Child );    // better not use, there's a caveat (1)

// approach w/o this caveat
std::shared_ptr<Base> pNewChild( new Child );    // or make_shared
myList2.push_back( pNewChild );

注意我在这里使用shared_ptr,如果更符合您的目的,您也可以使用unique_ptr,但不应使用原始ptrs:因此myList2拥有它shared_ptr元素和shared_ptr包含共享所有权中的对象(Base类型),myList2间接拥有您在列表中存储ptrs的Child个对象。由于原始ptrs不表示所有权,例如不明显谁负责摧毁它们。详细了解cppreference/list

(1)有一点需要注意,请参阅"Rule of Zero",但不会影响此示例。


如果您真的想通过选择Comparer类的某个地方来进行泛型编程,那么“必须坚持编译时”:您的列表类型(*p的类型)不应该固定为{{1}但是相当通用(使用模板),你使用的所有算法也必须是通用的。你不能(简单地*)混合泛型编程和类型的运行时选择,因为泛型编程就是在编译时创建代码。

*有一个允许它的黑客攻击,滥用RTTI因此非常慢。

答案 1 :(得分:0)

对类型使用模板,但对行为使用继承。提供一个构造函数,您可以在其中传递构造的比较器类(作为模板化的参数类型),如

list<PartitionT<CompareJobReady, CompareJobRunning> > *p = 
  new list<PartitionT<CompareJobReady, CompareJobRunning> >( 
    new CompareJobReadyEDFVD(), new CompareJobRunningEDFVD());