C++使用自定义比较函数初始化priority_queue

C++ initialize priority_queue with custom compare function

本文关键字:初始化 priority queue 函数 比较 自定义 C++      更新时间:2023-10-16

我有一个Dijkstra类,它使用带有自定义比较函数的priority_queue。我用using语句将队列命名为DijkstraPriorityQueue。在类构造函数中,我初始化队列。为此,我在 lambda 表达式中给出了比较函数。

对于第一个队列,PQ1 ,比较函数是{ return distTo[u] > distTo[v]; }的,这编译得很好,因为vector<float> distTo是类的成员。

但是对于第二个队列,PQ2,函数是{ return distTo2[u] > distTo2[v]; }的,其中vector<float> distTo2只是构造函数中的一个临时变量,并且不会编译。(我认为至少是这个原因)

此外,我随机尝试凭直觉将vector<float> distTo2更改为static vector<float> distTo2,并且它会编译,但是我认为这不是我想做的。我不熟悉函数中的静态变量,因为 Java 或 C# 中不存在静态变量。无论如何,什么是使以下代码编译并按预期工作的干净解决方案?

Dijkstra.h

class Dijkstra
{
public:
    Dijkstra();  
    ~Dijkstra();       
private:    
    vector<float> distTo;
};

迪克斯特拉.cpp

using DijkstraPriorityQueue = priority_queue<int, vector<int>, function<bool(int, int)>>;
Dijkstra::Dijkstra()
{           
    distTo = vector<float>(V, FLT_MAX);
    // Compiles fine
    DijkstraPriorityQueue PQ1 = DijkstraPriorityQueue([this](int u, int v)
        { return distTo[u] > distTo[v]; });
    vector<float> distTo2 = vector<float>(V, FLT_MAX);
    // Doesn't compile
    DijkstraPriorityQueue PQ2 = DijkstraPriorityQueue([this](int u, int v)
        { return distTo2[u] > distTo2[v]; });
}

编辑:

以下代码也可以编译。有什么线索吗?有人可以解释一下lambda表达式上的捕获是什么吗?或者在这种情况下我应该如何正确编写代码?

DijkstraPriorityQueue PQ2 = DijkstraPriorityQueue([distTo2](int u, int v)
    { return distTo2[u] > distTo2[v]; });

您的问题有两个主要方面:

  • 这个"捕获"是什么,为什么会出现错误?

  • 如何为优先级队列指定自定义比较函数?

这些方面将单独讨论得最为清晰。

不幸的是,呈现的(不完整的)示例代码不太适合讨论任何一个方面,所以我只是忽略它。

什么是 lambda 捕获。

请考虑以下代码:

#include <stdio.h>
struct S
{
    int a_;
    void foo() const
    {
        // Compiles nicely:
        [this]() -> void { printf( "%dn", a_ ); }();
        // Doesn't compile, oh why!:
        int b = 666;
        [this]() -> void { printf( "%dn", b ); }();
    }
};
auto main()
    -> int
{ S{ 42 }.foo(); }

MinGW g++ 5.1.0 提供以下诊断(编译错误):

x1.cpp:在 lambda 函数中:x1.cpp:14:44:错误:"b"未捕获         [这]() -> void { printf( "%d", b ); }();                                            ^X1.cpp:14:14:注意:lambda 没有捕获默认值         [这]() -> void { printf( "%d", b ); }();              ^x1.cpp:13:13:注意:此处声明"int b"         整数 b = 666;             ^

为了理解"未捕获",让我们手动实现 lambda,只需执行类似于编译器对它执行的操作的代码转换:

    void foo() const
    {
        // Compiles nicely:
        //[this]() -> void { printf( "%dn", a_ ); }();
        class Functor_a
        {
        private:
            S const* captured_this_;
        public:
            void operator()()
            { printf( "%dn", captured_this_->a_ ); }
            Functor_a( S const* this_capture )
                : captured_this_( this_capture )
            {}
        };
        Functor_a f_a{ this };
        f_a();
        // Doesn't compile, oh why!:
        int b = 666;
        // [this]() -> void { printf( "%dn", b ); }();
        class Functor_b
        {
        private:
            S const* captured_this_;
        public:
            void operator()()
            { printf( "%dn", b ); }
            Functor_b( S const* this_capture )
                : captured_this_( this_capture )
            {}
        };
        Functor_b f_b{ this };
        f_b();
    }
};

诊断现在更加清晰。由于Functor_b是一个类,并且由于C++中的类是完全独立的实体,因此其代码与特定调用foo()中的事物无关或访问。因此,编译器不接受对某些未指定b的引用,但请注意,如果您真的是指包含范围内的b,那么嘿,该名称b每次调用foo中引用不同的变量,并且不是一个有效的选择:

x2.cpp:在成员函数 'void S::foo() const::Functor_b::operator()()':x2.cpp:37:35:错误:使用局部变量和包含函数自动存储                 { printf( "%d", b ); }                                   ^x2.cpp:28:17:注意:此处声明"int b"             整数 b = 666;                 ^

一种解决方案是捕获,即将其复制到函子类实例中,如下所示:

        class Functor_b
        {
        private:
            int const captured_b_;
        public:
            void operator()()
            { printf( "%dn", captured_b_ ); }
            Functor_b( int const b_capture )
                : captured_b_( b_capture )
            {}
        };
        Functor_b f_b{ b };     // ← The capture.
        f_b();                  // ← Using the captured value.

或者,您可以捕获指向变量的指针,通过引用捕获。在这种情况下,指针仅在变量的生存期内有效。因此,您最好不要在那之后保留函子实例。

用 lambda 表示法表示,值的捕获可能如下所示:

[b]() -> void { printf( "%dn", b ); }();

或者像这样,使用一般捕获任何需要按值=

[=]() -> void { printf( "%dn", b ); }();

通过引用捕获,即指针,如下所示:

[&]() -> void { printf( "%dn", b ); }();

如何为std::priority_queue指定比较函数。

例如像这样:

#include <iostream>
#include <string>
#include <queue>
#include <vector>
using namespace std;
struct S
{
    string name;
    int birth_year;
};
auto main() -> int
{
    struct Age_sort
    {
        auto operator()( S const& a, S const& b )
            -> bool
        { return (a.birth_year < b.birth_year); }
    };
    using Q = priority_queue< S, vector<S>, Age_sort >;
    Q pq;
    pq.push( S{ "beta", 1980 } );
    pq.push( S{ "alfa", 1992 } );
    pq.push( S{ "charlie", 1971 } );
    while( not pq.empty() )
    {
        cout << pq.top().name << ' ' << pq.top().birth_year << endl;
        pq.pop();
    }
}