C++使用自定义比较函数初始化priority_queue
C++ initialize priority_queue with custom compare function
我有一个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();
}
}
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员