[英]Can I use std::upper_bound without an underlying container?
I have a range of integers [start, end]
and a non-decreasing monotonic function f(i)
. 我有一系列整数
[start, end]
和一个非递减单调函数f(i)
。 So conceptually, I have a non-decreasing sequence [f(start), f(start + 1), .. , f(end)]
. 所以从概念上讲,我有一个非递减序列
[f(start), f(start + 1), .. , f(end)]
。 Can I use std::upper_bound
on that sequence to find the first element i
in the range that holds f(i) > some_value
? 我可以使用
std::upper_bound
该序列找到的第一个元素i
在保存范围f(i) > some_value
?
Conceptually, I'd like something like this: 从概念上讲,我喜欢这样的事情:
std::upper_bound(start, end + 1, some_value, [&](int lhs, int rhs) {
return f(lhs) < f(rhs);
});
But this doesn't compile because start
and end + 1
do not meet the requirements of forward iterators . 但这不会编译,因为
start
和end + 1
不符合forward迭代器的要求。
The short answer is yes, since std::upper_bound
works on iterators, not on containers. 简短的回答是肯定的,因为
std::upper_bound
适用于迭代器,而不适用于容器。 But iterators themselves are instances of corresponding class (for example, std::vector<int>::iterator
or whatnot). 但迭代器本身是相应类的实例(例如,
std::vector<int>::iterator
或whatnot)。
If you construct some specific class that will meet the requirements of ForwardIterator
not being actually bound to some sort of container, while still meaning something (for example, if you want to generate your sequence procedurally), it should work just fine. 如果你构造一些特定的类来满足
ForwardIterator
的要求, ForwardIterator
不是实际上绑定到某种容器,虽然仍然有意义(例如,如果你想在程序上生成序列),它应该可以正常工作。
Note that simple integer will not do the trick. 请注意,简单整数不会起作用。 On the other hand, a class, whose objects hold the value of your function for a particular argument value (with some additional batteries), will.
另一方面,一个类,其对象为特定参数值(带有一些额外的电池)保存函数的值,将会。
There are basically two answers: 基本上有两个答案:
Would it work by the standard or would it work with all practical implementations of the STL? 它是按照标准工作还是适用于STL的所有实际实现?
By the standard, as TC pointed out already, there are some strict requirements on iterators, especially that *it
has to return a (possibly const) reference to value_type
(which we would satisfy by returning the reference to a member of the iterator), but we also need that for it1 == it2
, *it1
and *it2
are references bound to the same object, which is only possible if we have a distinct object for every number in the range. 根据标准,正如TC已经指出的那样 ,对迭代器有一些严格的要求,特别是
*it
必须返回一个(可能是const)对value_type
引用(我们通过返回对迭代器成员的引用来满足),但是我们还需要it1 == it2
, *it1
和*it2
是绑定到同一个对象的引用,只有当我们为该范围内的每个数字都有一个不同的对象时才有可能。
If you want to do use this idea in practice, I don't believe any implementation of std::upper_bound
or similar methods actually relies on this reference equality, so you could just use a class that encapsulates an integer as an iterator, only overloading the necessary methods . 如果你想在实践中使用这个想法,我不相信
std::upper_bound
或类似方法的任何实现实际上都依赖于这个引用相等,所以你可以只使用一个封装整数作为迭代器的类,只重载必要的方法 。 As far as I can see, boost::irange
fulfills these requirements 据我
boost::irange
, boost::irange
满足了这些要求
As you can see, this is not strictly standard-compliant, but I see no reason why any implementation of binary search should rely on such strong requirements for the iterator, if the underlying 'storage' is const anyway. 正如您所看到的,这不是严格符合标准的,但我认为没有理由为什么任何二进制搜索的实现都应该依赖迭代器的强大要求,如果底层的“存储”仍然是const。
No, not practically, but yes in practice, but no if you want to be practical. 不,不是实际,但在实践中是肯定的,但如果你想要实践则不是。
upper_bound
requires ForwardIterator . upper_bound
需要ForwardIterator 。 ForwardIterator requires that *
returns an actual reference, and that if two iterators are equal then they refer to the same object. ForwardIterator要求
*
返回的实际值, 并且如果两个迭代相等则它们指代相同的对象。
For a container-less iterator, this requires an insanely complex iterator that caches the values it returns in a shared global map of some kind. 对于无容器迭代器,这需要一个非常复杂的迭代器,它将它返回的值缓存在某种共享的全局映射中。 To make it half practical, note that the iterator requirements say very little about the lifetime of said reference;
为了使它变得一半实用,请注意迭代器要求对所述参考的寿命几乎没有说明; so you'd want to reference count and destroy said values as the iterators in question cease to exist.
所以你想要引用计数并销毁所述值,因为有问题的迭代器不再存在。
Such a solution requires synchronization, global state, and is significantly more expensive and complex than something like boost::integer_range
. 这样的解决方案需要同步,全局状态,并且比
boost::integer_range
更加昂贵和复杂。 No sane person would write this except as an exercise demonstrating why the standard needs to be fixed. 没有理智的人会写这个,除非作为演示标准需要修复的练习。
No sane implementation of upper_bound
actually requires that the iterators in question are full-scale forward iterators, barring one that does full concept-checks to validate against the standard (and not against what the actual algorithm needs). 没有合理的
upper_bound
实现实际上要求所讨论的迭代器是完整的前向迭代器,除非进行完整的概念检查以验证标准(而不是实际算法需要的)。 Input iterators with stability on the values returned almost certainly does it. 输入迭代器在返回的值上具有稳定性,几乎可以肯定。 There is no such concept in the C++ standard, and forward iterator is the weakest iterator category in the standard that satifies it.
在C ++标准中没有这样的概念,并且前向迭代器是标准中最弱的迭代器类别,它满足它。
This problem, of effectively demanding iterators be backed by containers, is a flaw in the standard in my opinion. 这个问题,有效地要求迭代器由容器支持,在我看来是标准的一个缺陷。 Container-free iterators are powerful and useful, except they rarely technically work in standard containers.
无容器迭代器功能强大且有用,除非它们在技术上很少在标准容器中工作。
Adding new iterator categories has proved problematic, because there is little way to do it without breaking existing code. 添加新的迭代器类别已经证明是有问题的,因为在不破坏现有代码的情况下几乎没有办法。 They looked into it for contiguous iterators, and wrote it off as impractical (I don't know all the details of what they tried).
他们调查它是为了连续的迭代器,并把它写下来是不切实际的(我不知道他们尝试过的所有细节)。
Adding new iterator concepts that are not backed by tags is more possible, but probably will have to wait until concepts are part of the C++ language and not just the standard; 添加没有标记支持的新迭代器概念是更有可能的,但可能必须等到概念是C ++语言的一部分而不仅仅是标准; then experimenting with adding new concepts becomes something you can specify in C++ instead of in standardese, which makes it far easier.
然后尝试添加新概念成为您可以在C ++而不是标准中指定的东西,这使得它更容易。
This does, however, result in an ill-formed program, no diagnostic required. 但是,这会导致程序格式错误,无需诊断。 So consider if it is worth it;
所以考虑是否值得; it may actually be easier to reimplement
upper_bound
than maintain a program whose every excution is undefined behavior, and every compile at the mercy of a compiler upgrade. 实际上,重新实现
upper_bound
比维护每个执行都是未定义行为的程序更容易,并且每次编译都受编译器升级的支配。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.