3

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

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> >();

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

4

2 回答 2

1

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

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

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

那么 的元素myList只能作为(引用)类型的对象来访问Base。您可以存储类型的元素Base(例如push_back, )并获取(例如,或通过迭代器)emplace_back的引用/副本,例如,请参阅cppreference/list。让我们来看看:frontpush_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这里使用 a,unique_ptr如果更适合您的目的,您也可以使用 a,但您不应该使用原始 ptrs:因为 thismyList2拥有它的shared_ptr元素并且 ashared_ptr拥有一个共享所有权的对象(类型为 Base),myList2因此间接拥有Child您在列表中存储 ptrs 的对象。由于原始 ptr 不表示所有权,因此谁负责销毁它们并不明显。阅读有关“零规则”的更多信息。

(1) 有一个警告,尽管它不影响这个例子,但请参阅boost 。


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

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

于 2012-11-08T15:55:56.067 回答
0

Use templating for the types, but inheritance for behaviour. Provide a constructor into which you can pass constructed comparator classes (as templated argument types), like

list<PartitionT<CompareJobReady, CompareJobRunning> > *p = 
  new list<PartitionT<CompareJobReady, CompareJobRunning> >( 
    new CompareJobReadyEDFVD(), new CompareJobRunningEDFVD());
于 2012-11-08T11:58:10.013 回答