我有一个这个结构的std :: vector:
struct MS
{
double aT;
double bT;
double cT;
};
我想使用std :: sort以及std :: lower_bound / equal_range等...
我需要能够对它进行排序并在结构的前两个元素中查找它。所以目前我有这个:
class MSaTLess
{
public:
bool operator() (const MS &lhs, const MS &rhs) const
{
return TLess(lhs.aT, rhs.aT);
}
bool operator() (const MS &lhs, const double d) const
{
return TLess(lhs.aT, d);
}
bool operator() (const double d, const MS &rhs) const
{
return TLess(d, rhs.aT);
}
private:
bool TLess(const double& d1, const double& d2) const
{
return d1 < d2;
}
};
class MSbTLess
{
public:
bool operator() (const MS &lhs, const MS &rhs) const
{
return TLess(lhs.bT, rhs.bT);
}
bool operator() (const MS &lhs, const double d) const
{
return TLess(lhs.bT, d);
}
bool operator() (const double d, const MS &rhs) const
{
return TLess(d, rhs.bT);
}
private:
bool TLess(const double& d1, const double& d2) const
{
return d1 < d2;
}
};
这允许我使用MSaTLess()调用std :: sort和std :: lower_bound来基于aT元素进行排序/查找,并使用MSbTLess()根据bT元素进行排序/查找。
我想摆脱仿函数并改用C ++ 0x lambdas。对于相对简单的排序,因为lambda将两个MS类型的对象作为参数。
但是对于lower_bound和其他二进制搜索查找算法呢?他们需要能够用(MS,双)参数调用比较器,反之亦然(双,MS),对吧?如何在调用lower_bound时最好地为lambda提供这些?我知道我可以创建一个MS虚拟对象,其中搜索了所需的键值,然后使用与std :: sort相同的lambda,但有没有办法在不使用虚拟对象的情况下完成它?
答案 0 :(得分:16)
这有点尴尬,但如果你从标准中检查lower_bound
和upper_bound
的定义,你会发现lower_bound
的定义将解除引用的迭代器作为< em>比较的第一个参数(和第二个值),而upper_bound
将解除引用的迭代器第二个(和值第一个)。
所以,我没有测试过这个,但我认为你想要:
std::lower_bound(vec.begin(), vec.end(), 3.142, [](const MS &lhs, double rhs) {
return lhs.aT < rhs;
});
和
std::upper_bound(vec.begin(), vec.end(), 3.142, [](double lhs, const MS &rhs) {
return lhs < rhs.aT;
});
这是非常令人讨厌的,如果没有查找更多的东西,我不确定你实际上有权假设实现只使用比较器的方式在文本中描述 - 这是结果的定义,而不是到达那里的手段。它对binary_search
或equal_range
无效。
在25.3.3.1中没有明确说明迭代器的值类型必须可以转换为T,但是这种算法的要求是 T (在这种情况下) ,double
)必须是LessThanComparable,而不是T必须与任何特定顺序的迭代器的值类型相当。
所以我认为最好总是使用比较两个MS结构的lambda(或仿函数),而不是将double作为值传递,而是将正确的字段设置为您正在寻找的值的虚拟MS传递为:
std::upper_bound(vec.begin(), vec.end(), MS(3.142,0,0), [](const MS &lhs, const MS &rhs) {
return lhs.aT < rhs.aT;
});
如果你不想给MS一个构造函数(因为你希望它是POD),那么你可以编写一个函数来创建你的MS对象:
MS findA(double d) {
MS result = {d, 0, 0};
return result;
}
MS findB(double d) {
MS result = {0, d, 0};
return result;
}
真的,现在有了lambda,为了这份工作,我们想要一个二元搜索版本,它需要一个“比较器”:
double d = something();
unary_upper_bound(vec.begin(), vec.end(), [d](const MS &rhs) {
return d < rhs.aT;
});
但是,C ++ 0x不提供它。
答案 1 :(得分:1)
算法std :: sort,std :: lower_bound和std :: binary_search采用比较容器的两个元素的谓词。任何比较两个MS对象并在它们按顺序返回true
的lambda都应该适用于所有三种算法。
答案 2 :(得分:0)
与你对lambdas的看法没有直接关系,但这可能是使用二进制搜索功能的一个想法:
#include <iostream>
#include <algorithm>
#include <vector>
struct MS
{
double aT;
double bT;
double cT;
MS(double a, double b, double c) : aT(a), bT(b), cT(c) {}
};
// template parameter is a data member of MS, of type double
template <double MS::*F>
struct Find {
double d;
Find(double d) : d(d) {}
};
template <double MS::*F>
bool operator<(const Find<F> &lhs, const Find<F> &rhs) {
return lhs.d < rhs.d;
}
template <double MS::*F>
bool operator<(const Find<F> &lhs, const MS &rhs) {
return lhs.d < rhs.*F;
}
template <double MS::*F>
bool operator<(const MS &lhs, const Find<F> &rhs) {
return lhs.*F < rhs.d;
}
int main() {
std::cout << (Find<&MS::bT>(1) < Find<&MS::bT>(2)) << "\n";
std::cout << (Find<&MS::bT>(1) < MS(1,0,0)) << "\n";
std::cout << (MS(1,0,0) < Find<&MS::bT>(1)) << "\n";
std::vector<MS> vec;
vec.push_back(MS(1,0,0));
vec.push_back(MS(0,1,0));
std::lower_bound(vec.begin(), vec.end(), Find<&MS::bT>(0.5));
std::upper_bound(vec.begin(), vec.end(), Find<&MS::bT>(0.5));
}
基本上,通过使用Find
作为值,我们不必提供比较器,因为Find
使用我们指定的字段与MS
进行比较。这与您在此处看到的答案相同:how to sort STL vector,但在这种情况下使用值而不是比较器。不确定它是否真的好用,但它可能是,因为它指定要搜索的值和要在单个短表达式中搜索的字段。
答案 3 :(得分:0)
我对std::equal_range
遇到了同样的问题,并想出了另一种解决方案。
我有一个指向在类型字段上排序的对象的指针集合。我需要找到给定类型的对象范围。
const auto range = std::equal_range (next, blocks.end(), nullptr,
[type] (Object* o1, Object* o2)
{
return (o1 ? o1->Type() : type) < (o2 ? o2->Type() : type);
});
虽然它比专用谓词效率低,因为它为我的集合中的每个对象引入了不必要的nullptr测试,但确实提供了一个有趣的替代方案。
顺便说一句,当我在你的例子中使用一个类时,我倾向于做以下事情。除了更短,这允许我添加其他类型,每种类型只有1个函数,而不是每种类型4个运算符。
class MSbTLess
{
private:
static inline const double& value (const MS& val)
{
return val.bT;
}
static inline const double& value (const double& val)
{
return val;
}
public:
template <typename T1, typename T2>
bool operator() (const T1& lhs, const T2& rhs) const
{
return value (t1) < value (t2);
}
};
答案 4 :(得分:0)
在definition of lower_bound和其他STL算法中,比较函数是这样的,第一种类型必须与前向迭代器的类型匹配,第二种类型必须与T的类型相匹配(即,值)。
template< class ForwardIt, class T, class Compare >
ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value, Compare comp );
因此,人们确实可以比较来自不同对象的事物(做其他响应称为一元比较器)。在C ++ 11中:
vector<MS> v = SomeSortedVectorofMSByFieldaT();
double a_key;
auto it = std::lower_bound(v.begin(),
v.end(),
a_key,
[]{const MS& m, const double& a) {
m.aT < a;
});
这也可以与其他STL算法函数一起使用。