std :: upper_bound()

时间:2015-08-28 17:01:10

标签: c++ stl c++-standard-library

我需要处理类型为Foo的对象列表,这些对象的共享质量对应于Bar的相同值。该列表是根据该质量进行预先排序的,因此我的想法是使用std::upper_bound来查找后续组的开始位置。

Bar FooToBar(const Foo &foo);
// sorted so that FooToBar(foolist[0] <= FooToBar(foolist[1]) <= ...
std::list<Foo> foolist; 

// find bounds of a group of Foo-s corresponding to someBar;
Bar someBar;
auto 
    groupBegin = foolist.begin(),
    // find last item of foolist whose FooToBar() == someBar
    groupEnd   = std::upper_bound( foolist.begin(), 
                                   foolist.end(), 
                                   someBar ); 

当然,这不起作用,因为FooBar无法直接比较。幸运的是,std::upper_bound的重载需要一个额外的比较器参数:

groupEnd = std::upper_bound( foolist.begin(), foolist.end(), someBar, Compare);

问题是,如何撰写Compare()?事情变得有趣。 cppreference.com说:

  

比较函数的签名应等同于以下内容:

     

bool cmp(const Type1&amp; a,const Type2&amp; b);

     

签名不需要const&amp;,但是函数对象不能修改传递给它的对象。   类型Type1和Type2必须使得类型为T的对象可以隐式转换为Type1和Type2,并且可以取消引用类型为ForwardIt的对象,然后将其隐式转换为Type1和Type2。

显然,我无法通过FooBar来满足这些条件。但是,cplusplus.com说了不同的内容:

  

接受两个参数的二进制函数(第一个是val,第二个是ForwardIterator指向的类型),并返回一个可转换为bool的值。

我可以使用它,所以:

bool Compare(const Bar &bar, const Foo &foo) { /* ... */ }

但是,这不能在VS2013中编译,也不能在g ++中编译:

  

/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/include/c ++ / bits / predefined_ops.h:141:37:错误:在参数传递中无法将'Foo'转换为'Bar'

奇怪的是,当我反转参数顺序时,它会按预期编译,运行和运行:

bool Compare(const Foo &foo, const Bar &bar) { /* ... */ }

所以它看起来像一个引用说了一件事,其他引用说了别的东西,编译器接受了一些不同的东西。或者我误解了什么?

2 个答案:

答案 0 :(得分:4)

您所指的是标准中的缺陷:#270。原来的措辞被认为是严格的(实际上,你提到了你的特定用例)。标准中的部分现在显示为[upper.bound]:

template<class ForwardIterator, class T>
  ForwardIterator
    upper_bound(ForwardIterator first, ForwardIterator last,
                const T& value);

template<class ForwardIterator, class T, class Compare>
  ForwardIterator
    upper_bound(ForwardIterator first, ForwardIterator last,
                const T& value, Compare comp);
     

需要e的元素[first,last)应根据表达式!(value < e)!comp(value, e)进行分区。
  返回i范围内的最远迭代器[first,last],对于j范围内的每个迭代器[first,i),以下相应条件成立:{ {1}}或!(value < *j)

在这两种情况下,comp(value, *j) == falsevalue的第一个参数,元素是第二个。所以以下是完全有效的代码:

comp

上面的内容适用于gcc 5.2(甚至4.6.4 - modulo lambda - 这是我最容易访问的lambda)和clang 3.6。

答案 1 :(得分:1)

如果您阅读了问题中引用的文档部分,您就会明白,除非存在从BarFoo的隐式转换,否则Compare的两个版本都是不正确。一个版本有效的事实仅仅是一个幸运的巧合。使用不同的编译器可能很容易失败。