我需要将派生的比较器传递给std::priority_queue
,但由于某种原因,基类'正在调用operator()。
以下是显示此行为的最小代码:
class Base {
public:
virtual bool operator() (int l, int r) const {
cout << "Should not be called" << std::endl;
return 0;
}
virtual ~Base() {}
};
class A : public Base {
public:
bool operator() (int l, int r) const override {
cout << "Should be called!!!!";
return l < r;
}
};
int main() {
priority_queue<int, vector<int>, Base> pq((A()));
pq.push(1);
pq.push(2);
pq.push(3);
pq.push(0);
cout << pq.top();
return 0;
}
The code is available on ideone as well
请注意,我无法使用priority_queue<int, vector<int>, A>
,因为我有Base
的其他子类,这将导致大量代码重复 1 。
我做错了什么?如何将比较器传递给将在其生命周期内使用的priority_queue?
(1)我知道我可以通过使用接受priority_queue<int,vector<int>, T>
的模板函数来绕过代码重复问题 - 但我真的不愿意。
答案 0 :(得分:8)
该标准将Compare comp
指定为 23.6.4.1 中类模板的值成员。据说构造者:
使用x初始化comp,使用y初始化comp(复制构造或移动 酌情建造);
因此,即使参数类型实际上是const Compare&
,您也会进行切片。
要解决这个问题,你可以为比较器实现一个pimpl-wrapper。这个包装器会在内部保持Base&
到实际的比较器,而在它的非虚拟operator()
中只需调用virtual operator()
/ Base
比较器的A
。
请仔细考虑A
对象的生命周期。根据比较器所需的状态,您可以在Base
中实施virtual clone
-method。并将Base
作为std::unique_ptr<Base>
保留在PimplCompare
中 - 您将其复制到其副本中。或者您将其保留为std::shared_ptr<Base>
。
答案 1 :(得分:5)
构造函数采用const Compare&
,在将对象传递给函数时不会导致任何切片,但我们在documentation
使用cont的内容复制构造底层容器c。复制 - 构造比较函子comp与compare的内容。
由于副本正在发生且模板类型为Base
,因此您只需要复制并存储Base
对象的A
部分。
您必须将比较对象包装在某种包装器中,并公开一个非虚拟operator ()
,它将调用传递给operator()
构造函数的类型的虚拟priority_queue
。 / p>