C ++中二进制搜索中的比较函数

时间:2016-09-09 05:37:05

标签: c++ c++11 lambda binary-search lower-bound

我正在研究一个C ++程序,其中指向class(航空类)的指针是排序向量中的对象。我想确定航空公司是否已经是向量中的指向对象。首先我使用lambda应用lower_bound,它是成功的。然后我用相同的lambda实现binary_search,但它失败了。错误消息如下,

__binary_search(_ForwardIterator __first, _ForwardIterator __last, const _Tp&     __value_, _Compare __comp)
{
   __first = __lower_bound<_Compare>(__first, __last, __value_, __comp);
   return __first != __last && !__comp(__value_, *__first);
}
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:4139:13: No matching function for call to object of type '(lambda at /Users/.../Desktop/Programming/C++/trial/trial/main.cpp:188:61)'

看起来lambda在二进制搜索中不起作用。你能帮我弄清楚它为什么没有?非常感谢!

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class vector_test{
public:
    vector_test(string name_) : name(name_) {}
    const string get_name() const {return name;}
private:
    string name;
};

typedef vector<vector_test*> vector_container;

int main()
{
    vector_container test;
    string name = "Delta";
    vector_test *vt1 = new vector_test{"Sothwest"};
    vector_test *vt2 = new vector_test{"Delta"};
    vector_test *vt3 = new vector_test{"Hawaii"};
    vector_test *vt4 = new vector_test{"United"};
    test = {vt2, vt3, vt1, vt4};
    auto iter = lower_bound(test.begin(), test.end(), name, [](vector_test* ptr, string name) {
        return  ptr->get_name()< name;});
    if (iter != test.end() && (**iter).get_name() == name)
        cout << "It exits!\n";
    else
        cout << "It doesn't exit!\n"
    auto it = binary_search(test.begin(), test.end(), name, [](vector_test* ptr, string name) {
        return name < ptr->get_name();});
    if (it)
        cout << "It exits!\n";
    else
        cout << "It doesn't exit!\n"
 }

3 个答案:

答案 0 :(得分:3)

您对lower_bound版本的调用,但是对binary_search的调用没有。这是因为预期比较函子的差异。

lower_boundis

  

类型Type1必须能够解除引用类型为ForwardIt的对象,然后隐式转换为Type1。类型Type2必须是类型为T的对象可以隐式转换为Type2。

binary_searchis

  

类型Type1和Type2必须使得类型为T的对象可以隐式转换为Type1和Type2,并且可以取消引用类型为ForwardIt的对象,然后将其隐式转换为Type1和Type2。

您的比较仿函数符合第一个要求,但不符合第二个要求。

您似乎错过的另一件事是lower_bound返回迭代器,但binary_search仅返回bool

考虑到所有事情,您最好在lower_bound使用driver.findElement(By.cssSelector("button.ui-datepicker-trigger.ui-button[aria-label='Show Calendar']")).click(); 。只需使用生成的迭代器来查看元素是否在逻辑上处于序列中。您可以使用this question接受的答案。

最后,正如BeyelerStudios在评论中非常正确地指出的那样,您应该确保lambda的内容与序列的顺序一致。

答案 1 :(得分:2)

您的问题是binary_search需要对称比较函数,其中LHS或RHS可以是范围的内容,也可以是您要比较的值。

这是一个问题,但不是一个难题。这是一个解决方案:

这是一种采用投影函数F和目标类型T的类型。

然后隐式地将任何可以隐式地或通过T投射到F的内容包装起来并将其包装起来:

template<class F, class T>
struct projector_t {
  void const*ptr = nullptr;
  T(*func)( F const* f, void const* ptr) = nullptr;

  // an X that is implicitly convertible to a T:
  template<class X,
    class dX = std::decay_t<X>,
    std::enable_if_t<
      !std::is_same<dX, projector_t>{}
      && std::is_convertible<X const&, T>{}
    , int> = 0
  >
  projector_t( X const& x ):
    ptr(std::addressof(x)),
    func([](F const*, void const* ptr)->T{
      return *static_cast<X const*>(ptr);
    })
  {}

  // an X that is not implicitly convertible to a T:
  template<class X,
    class dX = std::decay_t<X>,
    std::enable_if_t<
      !std::is_same<dX, projector_t>{}
      && !std::is_convertible<X const&, T>{}
    , int> = 0
  >
  projector_t( X const& x ):
    ptr(std::addressof(x)),
    func([](F const* f, void const* ptr)->T{
      auto px = static_cast<X const*>(ptr);
      return (*f)(*px);
    })
  {}
  projector_t( projector_t&& )=default;
  projector_t( projector_t const& )=default;
  projector_t& operator=( projector_t&& )=default;
  projector_t& operator=( projector_t const& )=default;

  T get( F const* f ) const {
    return func( f, ptr );
  }
};

我们现在可以编写采用投影并创建排序的代码:

template<class F, class T>
struct projected_order_t {
  F f;
  bool operator()( projector_t<F, T> lhs, projector_t<F, T> rhs ) const {
    return lhs.get(std::addressof(f)) < rhs.get(std::addressof(f));
  }
};
template<class T, class F>
projected_order_t<F, T> projected_order( F fin ) {
  return {std::move(fin)};
}

projected_order<T>(lambda)需要一个lambda。它返回一个比较函数对象。该对象有两个参数。如果传递了一个可以转换为T的对象,那么每个参数都只存储该转换。如果没有,它会尝试调用lambda将其转换为T。然后在此操作的结果上调用<

&#34;行动&#34;在构建T期间,func存储在成员变量projector_t中,而其运行的非F数据存储在void const* ptr中成员变量。要获得T,成员函数get会将F const*传递给ptr并传递给func

funcF应用于x,或隐式转换。

projetec_order_t提供一个operator(),需要两个projector_t,然后调用get传递它存储的F。这将提取代表每个参数的T。然后比较这些T

这项技术的一个好处是我们只需要提供投影,而不是比较,并且从投影中可以巧妙地推导出比较。

live example

一个简单的改进是允许它链接到不同的比较函数,默认为std::less<T>,而不是调用<

答案 2 :(得分:1)

当我在我的IDE VS2015中使用它进行测试并阅读编译器错误时,我注意到Ami Tavory打败了我回答这个问题。因此,这可能会为正在发生的事情提供一些见解或清晰。

在使用lower_bound()进行的第一次搜索中,它会按照Ami所述进行编译,并从搜索中返回容器的迭代器。

在你使用binary_search()进行的第二次搜索中,它不会编译,而且正如Ami所说,它只会返回一个bool,就像它被发现一样。至于它不编译这里是Visual Studio 2015 CE的编译器错误

1>------ Build started: Project: LambdaTemplates, Configuration: Debug Win32 ------
1>  LambdaTemplates.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(2709): error C2664: 'bool main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>::operator ()(vector_test *,std::string) const': cannot convert argument 1 from 'const std::string' to 'vector_test *'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(2709): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>  c:\users\skilz80\documents\visual studio 2015\projects\stackoverflowsolutions\lambdatemplates\lambdatemplates.cpp(46): note: see reference to function template instantiation 'bool std::binary_search<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<vector_test *>>>,std::string,main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>>(_FwdIt,_FwdIt,const _Ty &,_Pr)' being compiled
1>          with
1>          [
1>              _FwdIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<vector_test *>>>,
1>              _Ty=std::string,
1>              _Pr=main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>
1>          ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

它表示无法将参数1从const std::string转换为vector_test*

那么这里发生了什么?让我们抽象一下,让我们暂时将lambda写在搜索函数调用谓词参数列表之外。所以这段代码看起来像这样:

auto myLambda = []( vector_test* ptr, std::string name ) {
    return name < ptr->get_name();
};

auto it = std::binary_search( test.begin(), test.end(), name, myLambda );

if (it)
    std::cout << "It is here\n";
else
    std::cout << "It is NOT here\n";

现在让我们看一下编译错误:

1>------ Build started: Project: LambdaTemplates, Configuration: Debug Win32 ------
1>  stdafx.cpp
1>  LambdaTemplates.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(2709): error C2664: 'bool main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>::operator ()(vector_test *,std::string) const': cannot convert argument 1 from 'const std::string' to 'vector_test *'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(2709): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>  c:\users\skilz80\documents\visual studio 2015\projects\stackoverflowsolutions\lambdatemplates\lambdatemplates.cpp(45): note: see reference to function template instantiation 'bool std::binary_search<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<vector_test *>>>,std::string,main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>>(_FwdIt,_FwdIt,const _Ty &,_Pr)' being compiled
1>          with
1>          [
1>              _FwdIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<vector_test *>>>,
1>              _Ty=std::string,
1>              _Pr=main::<lambda_79759dd0460f5d162e02d2bb1cee5db7>
1>          ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

我们收到一条非常相似的错误消息。因此,请注释掉用于调用二进制搜索的代码行并检查编译器错误消息...

auto myLambda = []( vector_test* ptr, std::string name ) {
        return name < ptr->get_name();
    };

/*auto it = std::binary_search( test.begin(), test.end(), name, myLambda );

if (it)
    std::cout << "It is here\n";
else
    std::cout << "It is NOT here\n";
*/

现在我们没有任何编译器错误。所以lambda本身很好,但binary_search()函数中发生的是:

您正在传递2个前向迭代器beginend一个搜索词或值name,即std::string。您的前向迭代器是vector_test pointers的向量。这并不是说你的lambda是错误的,只是函数无法从作为你的搜索查询数据类型的std::string转换为包含指向vector_test个对象的指针的向量从而使这成为错误的lambda类型或错误的搜索参数。您的vector_test对象类不提供任何构造函数或转换因子,或重载运算符以转换为std :: string。另外,在使用binary_search时,您的容器应作为旁注,应预先排序。