无法将派生比较传递给 std::priority_queue

Unable to pass derived Compare to std::priority_queue

我需要将派生比较器传递给 std::priority_queue,但由于某些原因,正在调用基础 class' 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 的其他子 class,这将导致大量代码重复1 .

我做错了什么?如何将比较器传递给将在其生命周期内使用的 priority_queue?


(1) 我知道我可以通过使用接受 priority_queue<int,vector<int>, T> 的模板函数来绕过代码重复问题 - 但我真的不想这样做。

构造函数采用 const Compare&,在将对象传递给函数时不会导致任何切片,但我们在 documentation

Copy-constructs the underlying container c with the contents of cont. Copy-constructs the comparison functor comp with the contents of compare.

由于正在进行复制且模板类型为 Base,因此您只需复制并存储 A 对象的 Base 部分。

您必须将比较对象包装在某种包装器中,并公开一个非虚拟 operator (),它将调用传递给 priority_queue 构造函数的类型的虚拟 operator() .

该标准将 Compare comp 指定为 23.6.4.1 中 class 模板的值成员。据说构造函数:

Initializes comp with x and c with y (copy constructing or move constructing as appropriate);

因此你有切片,即使参数类型实际上是 const Compare&.

要解决这个问题,您可以为比较器实现一个 pimpl-wrapper。这个包装器将在内部保留一个 Base& 到实际的比较器,并且在它的非虚拟 operator() 中只需调用 Base / A 比较器的 virtual operator()

请仔细考虑您的 A 对象的生命周期。根据比较器所需的状态,您可以在 Base 中实现 virtual clone-method。并在您的 PimplCompare 中将 Base 作为 std::unique_ptr<Base> - 您将其克隆到它的复制者中。或者您将其保留为 std::shared_ptr<Base>.