我有一个关于模板参数的相当简单的问题。我正在编写一个类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> >();
但编译器告诉我转换是不可能的。什么是我的问题的最佳解决方案?
答案 0 :(得分:1)
标准库的容器类拥有其元素。也就是说,如果你有:
class Base {};
class Child : public Base {};
std::list < Base > myList;
std::list < Base > *pMyList;
然后myList
的元素只能作为(引用)类型为Base
的对象来访问。您可以存储Base
类型的元素(例如push_back
,emplace_back
)并获取(例如front
或通过迭代器)的引用/副本,例如,请参阅{{ 3}}。我们来看看push_back
:
void push_back(const value_type&);
value_type
是您传递给std::list
的第一个模板参数。在您的情况下,即PartitionT < CompareJobReady, CompareJobRunning >
,或在上面的示例中,它是Base
。
push_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());