On https://en.cppreference.com/w/cpp/ranges, std::views::counted is listed in the range adaptors section. However, it is not tagged as range adaptor object.
I guess that why I can't write using the pipe operator like:
std::vector<size_t> vec = {1, 2, 3, 4, 5};
auto view = vec | std::ranges::counted(... ; // does not compile
My questions are:
what is a std::ranges::counted? Why is it listed in the range adaptor section?
what are the use cases? what are the advantages over using take and drop?
Cppreference is following the organization of the C++20 standard. And it puts views::counted into the "Range Adaptors" section. Despite the fact that the standard says:
These adaptors can be chained to create pipelines of range transformations that evaluate lazily as the resulting view is iterated.
This is not true of the behavior of views::counted. Indeed, most of the other elements in that section say that their customization points "denotes a range adaptor object" (which describes the piping functionality), but views::counted does not.
It's unclear why they put it in that section, but it is a useful type in-and-of itself. It's really just an efficient way of saying subrange(it, it + n). It is efficient in that it doesn't actually increment the iterator by n.
The advantage it has over take_view is that take_view operates on a range, while all counted needs is an iterator. The main difference is that counted assumes that there are n valid iterator positions (and will give UB if that is not the case), while take_view does not. take_view will give you up to n objects, but if the range is shorter than that (as defined by the sentinel), it doesn't try to iterate past the end of the range.
According to the docs
A counted view presents a view of the elements of the counted range [i, n) for some iterator i and non-negative integer n.
A counted range [i, n) is the n elements starting with the element pointed to by i and up to but not including the element, if any, pointed to by the result of n applications of ++i.
So essentially it returns a slice given a starting iterator and a number of elements to include after that iterator. The example shown in the docs is
#include <ranges>
#include <iostream>
int main()
{
const int a[] = {1, 2, 3, 4, 5, 6, 7};
for(int i : std::views::counted(a, 3))
std::cout << i << ' ';
std::cout << '\n';
const auto il = {1, 2, 3, 4, 5};
for (int i : std::views::counted(il.begin() + 1, 3))
std::cout << i << ' ';
std::cout << '\n';
}
Output
1 2 3
2 3 4
Comparing the specific functions you listed, here are their summaries:
std::ranges::views::take: a view consisting of (up to) the first N elements of another view
std::ranges::views::drop: a view consisting of elements of another view, skipping (up to) the first N elements
std::ranges::views::counted: creates a subrange from an iterator and a count, always containing exactly N elements
views::counted is similar to views::take. However, the former accepts an iterator instead of a range.
One of the benefits of views::counted over views::take is that it allows us to construct a sized input/output range when we already know its size:
auto ints = views::istream<int>(std::cin);
auto counted = views::counted(ints.begin(), 4);
auto take = views::take(ints, 4);
static_assert(ranges::sized_range<decltype(counted)>); // ok
static_assert(ranges::sized_range<decltype(take)>); // failed
Unlike views::take, since we omit the sentinel information, we must ensure that the iterator is still valid after incrementing n times, while the former is guaranteed to always return a valid range.
Related
Hi I have a question on the usage of std::nth_element.
If I want to obtain the k-th largest element from a vector, should it inclusive or exclusive?
int k = 3;
vector<int> nums{1,2,3,4,5,6,7};
std::nth_element(nums.begin(), nums.begin()+k-1, nums.end(), [](int& a, int& b){return a > b;});
int result = nums[k-1]
or
int k = 3
vector<int> nums{1,2,3,4,5,6,7};
std::nth_element(nums.begin(), nums.begin()+k, nums.end(), [](int& a, int& b){return a > b;});
int result = nums[k-1]
It reminds me that when we get a subarray using iterator, it should be exclusive?
for example,
vector<int> sub(nums.begin(), nums.begin()+k);
So, the n for nth_element is also exclusive?
This is the type of question that is best to figure out and understand by playing around with a bunch of examples on your own.
However, I'll try to explain why you're getting the same answer in both of your examples. As explained in cppreference, std::nth_element is a partial sorting algorithm. It only guarantees that, given an iterator to an element n as its second argument:
All of the elements before this new nth element are less than or equal to the elements after the new nth element.
("Less than or equal to" is the default behavior if you don't pass a special comparison function.)
That means if you use nums.begin()+k-1 in one case and nums.begin()+k in another case as the second argument to std::nth_element, then in the latter case the partial sorting algorithm will include one additional item in the sort. In that case, you are dividing the vector between larger and smaller items at a spot one index higher than in the first case. However, the (default) algorithm only guarantees that each of the items in the "small half" of the vector will be smaller than each of the items in the "large half," not that the two halves are sorted within themselves.
In other words, if you've done a partial sort through nums.begin()+k, there is no guarantee that nums[k-1] will be the next-smallest (or in your case, the next-largest) number in the entire vector.
With certain inputs, like your {1, 2, 3, 4, 5, 6, 7}, or {9, 4, 1, 8, 5}, you do happen to get the same answers.
However, with many others, like {1, 4, 9, 8, 5}, the results do not match:
int k = 3;
vector<int> nums{1,4,9,8,5};
auto numsCopy = nums;
// First with +k - 1
std::nth_element(nums.begin(), nums.begin()+k-1, nums.end(), [](int& a, int& b){return a > b;});
// Then with only +k
std::nth_element(numsCopy.begin(), numsCopy.begin()+k, numsCopy.end(), [](int& a, int& b){return a > b;});
cout << nums[k-1]; // 5
cout << numsCopy[k-1]; // 9
Demo
Can you figure out why that is?
Also, to clearly answer your question about inclusive vs exclusive, as #Daniel Junglas pointed out in the comments, the second argument to std::nth_element is meant to point directly to the item you wish to be changed. So if it helps you, you can think of that as "inclusive." This is different from the third argument to std::nth_element, the end iterator, which is always exclusive since .end() points beyond the last item in the vector.
I'm trying to get information if below code is undefined behavior, I did read here.
In particular I want to be sure that iterator variable iter will be valid in cases where moved elements overlap destination range.
I think this is not good but can't confirm, and what would be better way to do this?
#include <vector>
#include <iostream>
int main()
{
// move 3, 4 closer to beginning
std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7 };
auto iter = std::move(vec.begin() + 2, vec.end() - 3, vec.begin() + 1);
std::cout << *iter << std::endl;
}
also there is no reference to what this iter is supposed to point at?
Yes it's well-formed to use std::move for this case.
(emphasis mine)
When moving overlapping ranges, std::move is appropriate when moving
to the left (beginning of the destination range is outside the source
range) while std::move_backward is appropriate when moving to the
right (end of the destination range is outside the source range).
And about the return value,
Output iterator to the element past the last element moved (d_first + (last - first))
So iter points to the element following the last element moved, i.e.
// elements of vec (after moved)
// 1, 3, 4, x, 5, 6, 7
// here ^
Note that
After this operation the elements in the moved-from range will still
contain valid values of the appropriate type, but not necessarily the
same values as before the move.
Here, x is such an unspecified value. This will probably happen to be another 4, but this shouldn't be relied upon. Subtle changes, such as a vector of strings instead of numbers, can cause different behavior. For example, the same operation on { "one", "two", "three", "four", "five", "six", "seven" } would probably result in it being "two" rather than the "four" you might expect from seeing the numeric example.
In boost::adaptors::filtered the filter function is used likeso:
std::vector<int> input;
input += 1,2,3,4,5,6,7,8,9;
boost::copy(
input | filtered(is_even()),
std::ostream_iterator<int>(std::cout, ","));
What is the effect of the pipe operator in this case? It is not defined for std::vector, is it an overload? If so, how does one effectively search for such operators in libraires like boost?
This is a Boost Range Adaptor. Some more documentation has been written in this online book "The Boost C++ Libraries".
There are many such ranges, which can be composed to write high-level functional effect. Examples are here:
Why is there no transform_if in the C++ standard library?
Is it possible to use boost::filter_iterator for output?
The filtered adaptor
input | filtered(is_even()) creates a temporary instance of the adaptor type:
boost::range_detail::filtered_range<is_even, std::vector<int> >
That's a type that models the Range Concept and holds a reference to the source range (input) as well as a copy of the filter predicate (is_even()).
The Range Concept is then implemented such that iteration of the range yields a bidirectional range as if it had only the elements of the source range that satisfy the filter predicate. You could equally write:
is_even predicate;
for (auto const& i : input)
if (predicate(i))
std::cout << i << ",";
range | adaptor is an expression template that generates a new adapted range.
How/Why Does It Work With |?
The real answer is "because that's how it's designed and documented". The more technical explanation is because filtered(is_even()) is of type boost::range_detail::filter_holder<is_even> which has an overloaded operator |:
template< class SinglePassRange, class Predicate >
inline filtered_range<Predicate, SinglePassRange>
operator|(SinglePassRange& r,
const filter_holder<Predicate>& f)
{
BOOST_RANGE_CONCEPT_ASSERT((SinglePassRangeConcept<SinglePassRange>));
return filtered_range<Predicate, SinglePassRange>( f.val, r );
}
Note: The assert only verifies the minimum required traversal category. As documented you'll get "The minimum of the range category of rng and Bidirectional Range", which is a Bidirectional Range in this case (because vector has random traversal).
The Other Thing
I am already lost at input += 1,2,.... – user463035818 56 mins ago
That's Boost Assign, unrelated to the range adaptor, I'd venture that C++11 makes it largely obsolete, because you can easily say
std::vector<int> input { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Or indeed
std::vector<int> input;
input.insert(input.end(), { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
I would like to perform a binary search on c++, on a sorted vector V.
In particular, I am not interested in finding a exact value of the entry of the vector. I would like to find the position j of the entry which satisfy V[j-1] <= X < V[j], where X is an input values.
for example:
for a vector v={1, 4, 7, 12, 17, 55} and X=8, the function should return 3.
Can I use the STD function binary_search, with O(log(2)) complexity?
And how?
Many thanks,
Al.
The standard functions for this are upper_bound and lower_bound. Read these
http://www.cplusplus.com/reference/algorithm/upper_bound/
http://www.cplusplus.com/reference/algorithm/lower_bound/
If you scroll down these pages a bit, you will find examples which should make things clear :)
A note on the functions Bartosz linked:
upper_bound(begin, end, value) returns the first element greater than the given value. This is the end (or one-past-the-end) of the interval where it could be inserted and preserve ordering
lower_bound(begin, end, value) return the first element not less than the given value. This is the beginning of the interval where it could be inserted
So in practice:
std::vector<int> v = {1, 4, 7, 12, 17, 55};
int x = 8;
auto first = std::lower_bound(v.begin(), v.end(), x);
auto last = std::upper_bound(first, v.end(), x);
should give first == last && *first == 12. This is because the half-open interval [first,last) where x could be inserted is empty.
Note that this is generally more useful than what you actually asked for, because
std::vector::insert(iterator, value)
inserts before the given iterator, so you can always use the result of upper_bound here.
Per your requirement, the correct STL function to use is upper_bound. Syntax:
upper_bound(V.begin(),V.end(),val)-V.begin()
Will return the 0 based index you seek.
I'm using a vector in a C++ program and I need to pass a part of that vector to a function.
If it was C, I would need to do the following (with arrays):
int arr[5] = {1, 2, 3, 4, 5};
func(arr+2); // Pass the part of the array {3, 4, 5}
Is there any other way than creating a new vector with the last part?
A common approach is to pass iterator ranges. This will work with all types of ranges, including those belonging to standard library containers and plain arrays:
template <typename Iterator>
void func(Iterator start, Iterator end)
{
for (Iterator it = start; it !=end; ++it)
{
// do something
}
}
then
std::vector<int> v = ...;
func(v.begin()+2, v.end());
int arr[5] = {1, 2, 3, 4, 5};
func(arr+2, arr+5);
Note: Although the function works for all kinds of ranges, not all iterator types support the increment via operator+ used in v.begin()+2. For alternatives, have a look at std::advance and std::next.
Generically you could send iterators.
static const int n[] = {1,2,3,4,5};
vector <int> vec;
copy (n, n + (sizeof (n) / sizeof (n[0])), back_inserter (vec));
vector <int>::iterator itStart = vec.begin();
++itStart; // points to `2`
vector <int>::iterator itEnd = itStart;
advance (itEnd,2); // points to 4
func (itStart, itEnd);
This will work with more than just vectors. However, since a vector has guaranteed contigious storage, so long as the vector doesn't reallocate you can send the addresses of elements:
func (&vec[1], &vec[3]);
The latest (C++20) approach is to use std::span. Create a std::span that views a part of std::vector and pass it to functions. Note: the elements must be continuous in memory to use std::span on a container, and std::vector is continuous in memory.
#include <span>
std::vector<int> int_vector = {1, 2, 3, 4, 5};
std::span<int> a_span(int_vector.data() + 2, int_vector.size() - 2);
for(const int a : a_span);
for(const int& a : a_span);
function(a_span);
As of C++20, I would make use of a range from the Ranges library, because it offers a variety of range adaptors for creating different views on you vector. For your use case, I would use the range adaptor std::views::drop as follows:
int main() {
std::vector<int> arr {1, 2, 3, 4, 5};
// Ignore the first two elements and pass only {3, 4, 5} to func().
func(arr | std::views::drop(2));
return 0;
}
This way you don't have to bother with interators or pointer/iterator arithmetic.
Also, no temporary vector is created for your shortened arr, because the view adaptor drop() creates a range that doesn't contain elements. The resulting range is just a view over the original vector arr, but with a customized iteration behavior.
For the declaration of func() I would use the placeholder type auto for the function parameter, because the type of the resulting range is quite complex. This makes func() a function template, though:
void func(auto range) {
for (int i : range)
std::cout << i << std::endl;
}
(Alternatively, you could pass arr by reference to func() and apply the range adaptor inside func().)
Output:
3
4
5
Code on Wandbox
std::vector<char> b(100);
send(z,&b[0],b.size(),0);
Try out this.
Read this too.
As some others already stated you can use iterators for that. You'll have to pass the start of the sequence and the end of the sequence to your worker function.
If you need more flexibility, you should take a look at slice. With slice you can for example retrieve every n-th entry of the vector.
I was also stuck to same problem.I found one really nice trick.Suppose you want to find the minimum in range L to R (both inclusive) of arr then you can do something like this:
vector<int>arr = {4,5,1,3,7};
int minVal = *min_element(begin(arr)+L,begin(arr)+(R+1));
Means you pass the complete array and range and then you can apply the above trick.