我有一个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]; });
答案 0 :(得分:2)
您的问题有两个主要方面:
这个“捕获”的东西是什么,为什么会出错?
如何为优先级队列指定自定义比较功能?
这些方面分别进行了最清晰的讨论。
不幸的是,所提出的(不完整的)示例代码不适合讨论任何一个方面,所以我只是忽略它。
请考虑以下代码:
#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();
}
}