我有一系列整数[start, end]
和一个非递减单调函数f(i)
。
因此,从概念上讲,我有一个非递减序列[f(start), f(start + 1), .. , f(end)]
。
我可以在该序列上使用std::upper_bound
来查找包含i
的范围内的第一个元素f(i) > some_value
吗?
从概念上讲,我想要这样的事情:
std::upper_bound(start, end + 1, some_value, [&](int lhs, int rhs) {
return f(lhs) < f(rhs);
});
但由于start
和end + 1
不符合forward iterators的要求,因此无法编译。
答案 0 :(得分:13)
简短的回答是肯定的,因为std::upper_bound
适用于迭代器,而不适用于容器。但迭代器本身是相应类的实例(例如,std::vector<int>::iterator
或诸如此类的。)
如果构造一些特定的类,它将满足ForwardIterator
未实际绑定到某种容器的要求,同时仍然具有某些意义(例如,如果你想在程序上生成序列),它应该工作得很好。
请注意,简单整数不会起作用。另一方面,一个类,其对象保存特定参数值的函数值(带有一些额外的电池),将会。
答案 1 :(得分:7)
基本上有两个答案:
它是按标准工作还是适用于STL的所有实际实现?
按照标准,已经T.C. pointed out,对迭代器有一些严格的要求,特别是*it
必须返回value_type
的一个(可能是const)引用(我们会满足通过返回对迭代器成员的引用,但我们还需要it1 == it2
,*it1
和*it2
是绑定到同一对象的引用,这只有在我们有该范围内每个数字的唯一对象。
如果你想在实践中使用这个想法,我不相信任何std::upper_bound
或类似方法的实现实际上依赖于这个引用相等,所以你可以只使用一个封装整数的类作为迭代器,只重载necessary methods。据我所知,boost::irange
符合这些要求
正如您所看到的,这不是严格符合标准的,但我认为没有理由为什么任何二进制搜索的实现都应该依赖于对迭代器的强烈要求,如果基础存储&#39;无论如何都是const。
答案 2 :(得分:3)
不,不是实际,但在实践中是肯定的,但如果你想要实用则不是。
upper_bound
需要ForwardIterator。 ForwardIterator要求*
返回实际引用和,如果两个迭代器相等,则它们引用相同的对象。
对于无容器迭代器,这需要一个非常复杂的迭代器,它将它返回的值缓存在某种共享的全局映射中。为了使它变得一半实用,请注意迭代器要求对所述参考的寿命几乎没有说明;所以你想引用计数并销毁所述值,因为有问题的迭代器不再存在。
这样的解决方案需要同步,全局状态,并且比boost::integer_range
更加昂贵和复杂。没有理智的人会写这个,除非作为演示为什么标准需要修复的练习。
upper_bound
没有理智的实现实际上需要所讨论的迭代器是全面的前向迭代器,除非进行完整的概念检查以验证标准(而不是针对标准)实际的算法需要什么)。输入迭代器在返回的值上具有稳定性,几乎可以肯定。在C ++标准中没有这样的概念,并且正向迭代器是标准中最弱的迭代器类别,可以满足它。
这个问题,有效地要求迭代器由容器支持,在我看来是标准的一个缺陷。无容器迭代器功能强大且非常有用,除非它们在技术上很少使用标准容器。
添加新的迭代器类别已经证明是有问题的,因为在不破坏现有代码的情况下几乎没有办法。他们查看了连续的迭代器,并把它写成不切实际(我不知道他们尝试过的所有细节)。
添加没有标记支持的新迭代器概念更有可能,但可能必须等到概念是C ++语言的一部分而不仅仅是标准;然后尝试添加新概念就可以用C ++而不是标准来指定,这使得它更容易。
然而,这会导致程序格式错误,无需诊断。所以考虑是否值得;重新实现upper_bound
实际上可能比维护每个执行都是未定义行为的程序更容易,并且每次编译都受编译器升级的支配。