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

时间:2016-03-05 01:42:54

标签: c++ priority-queue

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

对于第一个队列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;
};

Dijkstra.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]; });

1 个答案:

答案 0 :(得分:2)

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

  • 这个“捕获”的东西是什么,为什么会出错?

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

这些方面分别进行了最清晰的讨论。

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

什么是lambda捕获。

请考虑以下代码:

#include <stdio.h>

struct S
{
    int a_;

    void foo() const
    {
        // Compiles nicely:
        [this]() -> void { printf( "%d\n", a_ ); }();

        // Doesn't compile, oh why!:
        int b = 666;
        [this]() -> void { printf( "%d\n", b ); }();
    }
};

auto main()
    -> int
{ S{ 42 }.foo(); }

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

x1.cpp: In lambda function:
x1.cpp:14:44: error: 'b' is not captured
         [this]() -> void { printf( "%d\n", b ); }();
                                            ^
x1.cpp:14:14: note: the lambda has no capture-default
         [this]() -> void { printf( "%d\n", b ); }();
              ^
x1.cpp:13:13: note: 'int b' declared here
         int b = 666;
             ^

要理解“未捕获”,让我们手动实现lambdas ,只需进行与编译器相同的代码转换:

    void foo() const
    {
        // Compiles nicely:
        //[this]() -> void { printf( "%d\n", a_ ); }();
        class Functor_a
        {
        private:
            S const* captured_this_;

        public:
            void operator()()
            { printf( "%d\n", 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( "%d\n", b ); }();
        class Functor_b
        {
        private:
            S const* captured_this_;

        public:
            void operator()()
            { printf( "%d\n", 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: In member function 'void S::foo() const::Functor_b::operator()()':
x2.cpp:37:35: error: use of local variable with automatic storage from containing function
                 { printf( "%d\n", b ); }
                                   ^
x2.cpp:28:17: note: 'int b' declared here
             int b = 666;
                 ^

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

        class Functor_b
        {
        private:
            int const captured_b_;

        public:
            void operator()()
            { printf( "%d\n", 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( "%d\n", b ); }();

或者像这样,通过一般性捕获 - 无论需要按价值=

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

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

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

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

E.g。像这样:

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