Given the following code,
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::any_of(std::begin(numbers), std::end(numbers),
[](int number) { return number > 3; } );
is std::any_of required (by the standard) to return as soon as it reaches 4?
The standard itself doesn't place any such hard requirement. But one may infer it is indirectly encouraged ([alg.any_of]):
template <class InputIterator, class Predicate>
bool any_of(InputIterator first, InputIterator last, Predicate pred);
template <class ExecutionPolicy, class ForwardIterator, class Predicate>
bool any_of(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last,
Predicate pred);
Returns: false if [first, last) is empty or if there is no iterator i in the range [first, last) such that pred(*i) is true, and true
otherwise.
Complexity: At most last - first applications of the predicate.
While a perfectly conforming implementation may apply the predicate exactly last-first times, the wording to me sounds like it would be encouraged to exit as soon as possible.
Note that it's virtually impossible to ask the same of the overload that accepts an ExecutionPolicy. Since then the order of evaluation is not known.
On a less formal note, any implementation of the sequential version that does not exit the moment the predicate is true, puts the credentials of its author into question.
Related
1.
In the current standard draft the specification of std::stable_partition is:
template<class BidirectionalIterator, class Predicate>
BidirectionalIterator stable_partition(
BidirectionalIterator first, BidirectionalIterator last, Predicate pred);
I didn't find the requirement that BidirectionalIterator should be a bidirectional iterator, but the name suggests so. (See below)
2.
In the SGI STL, the specification is:
template <class ForwardIterator, class Predicate>
ForwardIterator stable_partition(
ForwardIterator first, ForwardIterator last, Predicate pred);
Requirements on types: ForwardIterator is a model of Forward Iterator.
The specification of complexity is the same for both, current standard and SGI, versions: at most N log N swaps, only O(N) swaps if there is enough extra memory and exactly N applications of the predicate and projection.
3.
The declaration in libstdc++ and libc++ looks like:
template<typename ForwardIterator, typename Predicate>
ForwardIterator stable_partition(
ForwardIterator first, ForwardIterator last, Predicate pred);
In GCC and Clang std::stable_partition indeed works with forward iterators. For example:
int main() {
std::forward_list<int> list{1, 4, 5, 2, 3, 0};
std::stable_partition(list.begin(), list.end(), [](int i) { return i < 3;});
for (auto v : list)
std::cout << v << ' ';
}
compiles and produces the correct output. Microsoft's compiler fails to compile this code (no -- operator). Intel's one succeeds.
I have two related questions:
Does std::stable_partition accepts at least bidirectional iterators by the standard or the name BidirectionalIterator is misleading?
If it indeed accepts only bidirectional iterators, why the support of forward iterators has been dropped?
Edit.
Found this clause:
If an algorithm's template parameter is named BidirectionalIterator, BidirectionalIterator1, or BidirectionalIterator2, the template argument shall meet the Cpp17BidirectionalIterator requirements.
So, only the second question remains.
First of all, no support has been dropped, std::stable_partition has always required BidirectionalIterator by the standard. Which does not mean that the implementors of the library are disallowed to give less restrictions on input arguments (if it continues to comply with other parts of the standard ofc). And so Gcc, Clang and Intel use their rights and made the code even more generic. You can think of it as a compiler extension of standard library.
Having said that one may ask why standard requires BidirectionalIterator here. I suppose it is possible because the authors of the standard didn't see a way to comply with the complexity requirements without this requirement. It is possible that the authors of gcc found a way to do it better than anticipated by the standard. Looking at the gcc source code kinda confirms this. https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_algo.h#L1613
EDIT:
I have dug through GCC library implementation and I think I get it. Implementation for ForwardIterator, BidirectionalIterator and RandomAccessIterator are different for std::stable_partition. This is due to different implementations of std::rotate which is used by the partitioning algorithm. And so, for forward iterator number of swaps is greater and can exceed (last - first) * log(last - first).
Look here and here.
The problem seems to have historical and not mathematical reasons. Looking through Alexander Stepanov's papers, I found this one: "Partition and Related Functions".
It contains the following passage:
Remark: It is interesting that this excellent algorithm is not in the C++ standard which requires bidirectional iterators for partition. I had known, implemented, and taught this algorithm for quite some time – since I first read about it in Bentley’s column in CACM in the mid-eighties. But my original STL proposal does, somehow, specify bidirectional iterators for both partition and stable_partition. Both of them were corrected in SGI STL, but most vendors are still behind. This little thing has been bothering me for over 10 years now; the most bothersome part being the fact of omission. How did it happen? I suspect that the explanation is quite simple: while in the early 90ties I already understood the idea of reducing every algorithms to its minimal requirements, and I also knew that the same operation could be implemented using better algorithms when we know more about the data to which they are applied, I was not yet fully aware of the need to provide an algorithm for the weakest case, if such an algorithm is available. It took several more years to understand the importance of “filling the algorithmic space.”
The following simple algorithm
template <typename I, // I models Forward Iterator
typename N, // N models Integer
typename P> // P models Unary Predicate
pair<I, I> stable_partition_inplace_n(I f, N n, P p)
{
if (n == 0) return make_pair(f, f);
if (n == 1) {
I l = successor(f);
if (p(*f)) l = f;
return make_pair(f, l);
}
pair<I, I> i = stable_partition_inplace_n(f, n/2, p);
pair<I, I> j = stable_partition_inplace_n(i.second, n – n/2, p);
return make_pair(rotate(i.first, i.second, j.first), j.second);
}
is given in that paper. It works with forward iterators and performs O(N log N) swaps in the worst case.
Consider I had an std::vector such that its contents were std::strings and {"a","b","c"}. If I were to perform std::find() looking for "a", would it stop once it iterated over "a" (ie. short-circuit) or continue to the end?
std::vector<std::string> vec;
vec.insert(vec.begin(), "a");
vec.insert(vec.begin(), "b");
vec.insert(vec.begin(), "c");
Which is faster?
std::find(vec.begin(), vec.end(), "a");
std::find(vec.begin(), vec.end(), "c");
See the possible implimenation of std::find
template<class InputIt, class T>
constexpr InputIt find(InputIt first, InputIt last, const T& value)
{
for (; first != last; ++first) {
if (*first == value) { // if the condition met
return first; // ---> here returns the iterator
}
}
return last;
}
It will stop iterating, once it finds the match.
Based on description here, yes it does.
Returns the first element in the range [first, last) that satisfies
specific criteria.
Complexity: At most last - first applications of the predicate
And by taking a look at its possible implementations it is stated that std::find uses short circuit
The C++17 standard draft defines the behavior of std::find in [alg.find] (only part relevant to overload used in question cited):
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last,
const T& value);
[...]
Returns: The first iterator i in the range [first, last) for which the following corresponding conditions hold: *i == value, [...]. Returns last if no such iterator is found.
Complexity: At most last - first applications of the corresponding predicate.
Previous standard versions, including C++03, contain basically the same wording.
Nothing in this guarantees that the elements are searched in any specific order at all or that std::find must stop testing the predicate once it found a match.
However, since the function must return the first iterator in the range satisfying the condition, it makes no sense to test out-of-order, because if a match was found, all previous iterators would need to be tested for an earlier match anyway.
Once a match is found it is also pointless to continue applying the predicate and the standard only requires the predicate to be applied "at most" as often as there are elements in the range.
Therefore any reasonable sequential implementation of std::find will search the iterator range in-order and return when a match is found. If an implementation did not do that, users would complain about it as soon as they noticed. Standard library implementors want their code to be fast where possible and they have no benefit letting their code do more work than necessary.
I suppose though that an implementation could make use of parallelization if it knows that this will not cause data races for the given types and in that case it might happen that the search examines iterators beyond the first match. Whether something like this is implemented in any standard library for the non-parallel std::find overload from the question, I don't know.
I was told here that:
The order of generate is not guaranteed => depending on the implementation
I have looked up gcc's implementation of generate:
for (; __first != __last; ++__first)
*__first = __gen();
And Visual Studio implements it identically to that. This is a relief to me as using a lambda in generate that reads and writes to a capture could have undeterministic results:
int foo[] = {1, 0, 13};
vector<int> bar(3);
generate(bar.begin(), bar.end(), [&]() {
static auto i = 0;
static auto total = 0;
total += foo[i];
return foo[i] / total;
});
I expect bar to contain {1, 0, 0}.
If I am allowed to execute out of order this could even cause a divide by 0 error.
So I'd sleep easier if this question could be answered with proof that generate is required to execute sequentially.
As a note here, I know that experimental::parallel::generate will not be sequential. I'm just asking about generate.
I was intrigued by this so have done some research.
At the bottom of this answer is a copy of the relevant section from the standard as of 2011.
You will see from the template declaration of std::generate<> that the iterator parameters must conform to the concept of a ForwardIterator and OutputIterator respectively.
A ForwardIterator does not support random access. it can only read or write sequentially. An OutputIterator is even more restrictive - its operator* implicitly includes the effect of an operator++. This is an explicit feature of the concept.
Therefore, by implication, the implementation of this function must access elements sequentially (and therefore generate values sequentially) since to not do so would break the contract implicit in the interface.
Therefore the standard does (implicitly and quietly) guarantee that std::generate initialise its elements sequentially. It would be impossible to write a well-formed implementation of std::generate that did not.
QED
25.3.7 Generate [alg.generate]
template<class ForwardIterator, class Generator>
void generate(ForwardIterator first, ForwardIterator last,
Generator gen);
template<class OutputIterator, class Size, class Generator>
OutputIterator generate_n(OutputIterator first, Size n, Generator gen);
1 Effects: The first algorithm invokes the function object gen and
assigns the return value of gen through all the iterators in the range
[first,last). The second algorithm invokes the function object gen and
assigns the return value of gen through all the iterators in the range
[first,first + n) if n is positive, otherwise it does nothing.
2
Requires: gen takes no arguments, Size shall be convertible to an
integral type (4.7, 12.3).
3 Returns: generate_n returns first + n for
non-negative values of n and first for negative values.
4 Complexity:
Exactly last - first, n, or 0 invocations of gen and assignments,
respectively.
Here is all the standard says about it (25.7.3/1):
template<class ForwardIterator, class Generator>
void generate(ForwardIterator first, ForwardIterator last, Generator gen);
template<class OutputIterator, class Size, class Generator>
OutputIterator generate_n(OutputIterator first, Size n, Generator gen);
The first algorithm invokes the function object
gen
and assigns the return value of
gen
through
all the iterators in the range
[first,last)
. The second algorithm invokes the function object
gen
and assigns the return value of
gen
through all the iterators in the range
[first,first + n)
if
n
is
positive, otherwise it does nothing.
As you can see, it is not explicitly stated that the iterator assignments must be done sequentially.
ISO C++11 24.3:
template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n);
// ...
template <class ForwardIterator>
ForwardIterator next
(
ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1
);
Why std::next does not accept InputIterators?
One of legal use cases I am thinking about is:
first = find(next(first, x), last, 11); // ...
I have found appropriate DR:
next/prev return an incremented iterator without changing the value of the original iterator. However, even this may invalidate an InputIterator. A ForwardIterator is required to guarantee the 'multipass' property.
But I don't understand how multipass/invalidation is related to that. Using same multipass/invalidation reasoning, we can even ban std::find for InputIterators:
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);
There is nothing special about std::next in compare to std::find or std::vector::insert(pos, first, last) which have perfectly legal use cases for InputIterators
Moreover std::next(it, n) can be used in generic code, which operates not only on InputIterators.
In effect, input iterators cannot be usefully copied, because once an input iterator is incremented, any copy left lying around is invalidated.
std::next takes an iterator and returns another iterator which has been advanced n times. You can't do that with an input iterator without invalidating the original iterator, which makes std::next pointless. By constrast, std::advance advances the specified iterator n times, which is fine with an input iterator.
std::next is the iterator generalization of operator+(T*, size_t). std::advance is the iterator generalization of operator+=(T*&, size_t). It may well be that std::advance, like operator+=, should return a reference instead of void.
It's true that there is a similar issue with std::find (and related functions); they, too, will invalidate any copy of the specified input iterators. But it is quite possible that the committee found that issue less serious.
According to most C++ references, for instance cplusplus.com, forward iterators are not required to be assignable (I mean, deferenced to an lvalue). However, for several STL algorithms that need to write values, for instance std::fill (also std::generate etc.), the specification uses forward iterator:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
while the equivalent behavior requires lvalue dereference:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val)
{
while (first != last) {
*first = val;
++first;
}
}
So, it is actually using a mutable forward iterator with a single pass.
Now the questions are:
(1) Why not make it clear that the forward iterators used in these cases are mutable?
(2) Update: I found the following question to be stupid: I temporarily forgot that output iterators do not need to support equality comparison. The above question remains, anyway.
Why use forward iterators instead of output iterators for std::fill, std::generate etc. while they do not actually need multiple passes? (std::copy only needs output iterators, for instance. What's the rationale?)
From the signature
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
you cannot infer that ForwardIterator is an iterator described in forward iterator. However, if you read the parameter description, you will find that first and last must be
Forward Iterators to the initial and final positions in a sequence of elements that support being assigned a value of type T.
(emphasis by me). So a forward iterator that fulfills nothing more than what is required of a forward iterator is not a valid argument.
It doesn't seem terribly strange to me, given that the specification for fill is that the (dereferenced) iterator be assignable from T. An output iterator won't suffice because it's not comparable to determine the range end, so a forward_iterator with requirements was chosen.
You'll note that fill_n does use output iterators because no iterator comparison is needed to determine the end of the sequence to fill.