In C++, is it possible to pass in an iterator that iterates only elements satisfying certain conditions? While it is certainly possible to write my own iterator class, I'm wondering there exists a standard library solution. For example, the constructor of std::discrete_distribution takes an input of two iterators (begin and end). Now I would need to do the following:
std::vector<int> x = {1, 2, 3, 4, 5};
std::vector<int> y;
std::copy_if(x.begin(), x.end(), std::back_inserter(y), my_condition);
std::discrete_distribution dd(y.begin(), y.end());
But this would require to copy the elemnts of y twice. I would prefer something like following:
std::vector<int> x = {1, 2, 3, 4, 5};
std::discrete_distribution dd(std::condition_iter(x.begin(), my_condition), x.end());
There is neither special syntax nor standard library feature in C++ which could solve your problem. However, there is a library called "range-v3" that might help. Here are a couple of links:https://github.com/ericniebler/range-v3https://ericniebler.github.io/range-v3/Look for view::remove_if - I think this is what you need. This will filter the container on the fly without actually modifying anything.
Related
Add element to function in place C++
Python code:
l1 = [1, 2, 3]
l2 = l1 + [4]
print(l2) #[1, 2, 3, 4]
l3 = l1+l2
print(l3) #[1, 2, 3, 1, 2, 3, 4]
Is there a way to do this in C++
You clarified in a comment that you want to do it "in place". In C++17 there isn't an operator or function in the standard library for concatenating two vectors (or containers), but it is pretty easy to create one yourself:
template <class T>
std::vector<T> concat(const std::vector<T>& v1, const std::vector<T>& v2)
{
auto res = v1;
res.insert(res.end(), v2.begin(), v2.end());
return res;
}
In C++20 all this changes as we will get ranges.
Until then you can use the ranges-v3 library (on which the standard ranges are based):
views::concat(v1, v2);
This returns a range. That should be enough as you should take range views as parameters or at least iterators. The advantage is that views are lazy. They don't do the actual transformations until you iterate over them and don't create new containers. If you however still need a vector you can do that like this:
#include <range/v3/view.hpp>
using namespace ranges;
auto test()
{
std::vector v1{10, 20, 30};
std::vector v2{1, 2, 3, 4, 5};
auto v = views::concat(v1, v2) | to<std::vector>();
}
There has been a comment about creating an overloaded operator+ instead of the function. That would be a bad idea for a few reasons:
Overloading an operator with only standard classes as parameters is a bad idea as you can't open namespace std to define it there and defining it elsewhere will cause problems with ADL.
Second, even if you could, a golden rule is to overload an operator to do expected things. In this case, an operator+ would be expected by (some/most) people to do element-wise addition.
I believe you're trying to add an element to a vector in c++.
vector<int> v; // { }
v.push_back(10); // { 10 }
To append one vector to another vector, you could use insert
vector<int> v = { 11, 12 };
vector<int> v2 = { 13, 14 };
v.insert(v.end(), v2.begin(), v2.end()); // { 11, 12, 13, 14 }
The first parameter to insert is an iterator pointing to where you want the second vector's elements to be inserted. The 2nd and 3rd parameters are the iterators pointing to the beginning and end of the second vector, they denote the boundaries of which elements to insert into the first vector.
This operation effectively appends the second vector into the first vector.
All C++ standard library containers have an insert() method; yet they don't all have a remove() method which does not take any arguments, but performs the cheapest possible removal at arbitrary order. Now, of course this would act differently for different containers: In a vector we would remove from the back, in a singly-list list we'd remove from the front (unless we kept a pointer to the tail), and so forth according to implementation details.
So, is there a more idiomatic way to do this other than rolling my own template specialization for every container?
Part of the design of the standard containers is that they ONLY provide operations as member functions if it is possible to provide one that is optimal (by chosen measures) for that container.
If a container provides a member function, that is because there is some way of implementing that function is a way that is optimal for that container.
If it is not possible to provide an optimal implementation of an operation (like remove()) then it is not provided.
Only std::list and (C++11 and later) std::forward_list are designed for efficient removal of elements, which is why they are the only containers with a remove() member function.
The other containers are not designed for efficient removal of arbitrary elements;
std::array cannot be resized, so it makes no sense have have either an insert() OR a remove() member function.
std::deque is only optimised for removal at the beginning or end.
Removal of an element from std::vector is less efficient than other containers, except (possibly) from the end.
Implementing a remove() member function for these containers therefore goes against the design philosophy.
So if you want to be able to efficiently remove elements from a container, you need to pick the right container for the job.
Rolling your own wrapper for the standard containers, to emulate operations that some containers don't support is simply misleading - in the sense of encouraging a user of your wrapper classes to believe they don't need to be careful with their choice of container if they have particular requirements of performance or memory usage.
So to answer your question
"So, is there a more idiomatic way to do this other than rolling my own template specialization for every container?
There are lot of ways to do remove
Sequence container and unordered container's erase() returns the next
iterator after the erased item.
Associative container's erase() returns nothing.
/*
* Remove from Vector or Deque
*/
vector<int> vec = {1, 4, 1, 1, 1, 12, 18, 16}; // To remove all '1'
for (vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr) {
if ( *itr == 1 ) {
vec.erase(itr);
}
} // vec: { 4, 12, 18, 16}
// Complexity: O(n*m)
remove(vec.begin(), vec.end(), 1); // O(n)
// vec: {4, 12, 18, 16, ?, ?, ?, ?}
vector<int>::iterator newEnd = remove(vec.begin(), vec.end(), 1); // O(n)
vec.erase(newEnd, vec.end());
// Similarly for algorithm: remove_if() and unique()
// vec still occupy 8 int space: vec.capacity() == 8
vec.shrink_to_fit(); // C++ 11
// Now vec.capacity() == 4
// For C++ 03:
vector<int>(vec).swap(vec); // Release the vacant memory
/*
* Remove from List
*/
list<int> mylist = {1, 4, 1, 1, 1, 12, 18, 16};
list<int>::iterator newEnd = remove(mylist.begin(), mylist.end(), 1);
mylist.erase(newEnd, mylist.end());
mylist.remove(1); // faster
/*
* Remove from associative containers or unordered containers
*/
multiset<int> myset = {1, 4, 1, 1, 1, 12, 18, 16};
multiset<int>::iterator newEnd = remove(myset.begin(), myset.end(), 1);
myset.erase(newEnd, myset.end()); // O(n)
myset.erase(1); // O(log(n)) or O(1)
I have some (templated) iterator pair Iterator begin1 and Iterator end1. I have a function foo, with signature
template <typename ForwardIterator>
void foo(ForwardIterator begin, ForwardIterator end);
which does something on that range. Now, I want to have foo act not on the actual range elements, but rather on a transformation of these elements by some lambda or some function bar. I can't use std::transform or otherwise use temorary storage for the transformed values, since that would take up too much space or since I'm not allowed to allocate anything on the heap.
Now, I know I can implement some kind of adapting iterator myself, but I'd rather use something ready-made and not reinvent the wheel.
So, what's a quick and dirty way to get an appropriate adapting iterator pair I can pass for foo to do what I want? i.e. for me to be able to call
foo(magic(begin), magic(end))
or
auto new_pair = magic(begin, end);
foo(new_pair.first, new_pair.second);
?
Eric Niebler's range-v3 library provides a very nice solution for this, and is planned to be included in a future standard. Here's a simple example:
std::vector<int> vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | view::transform([](int x){ return x % 2; });
foo(rng.begin(), rng.end()); // When foo dereferences these iterators, it
// will see: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0].
This is all lazy and efficient, by the way. :)
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.
guys!
I'm trying to understand the work of iterators, so in the code below,is it possible to change back_inserter to front_inserter without changing underlying data (structure).
Could you,please,explain why. If change is possible consider its key idea.
int a1[] = { 0, 1, 2, 3, 4, 5, 6 };
int a2[] = { 1, 4, 5 };
std::vector<int> a3;
int a4[] = { 0, 2, 3, 6 };
std::set_difference(a1, a1 + 7, a2, a2 + 3, std::back_inserter(a3));
assert(std::equal(a3.begin(), a3.end(), a4));
Thank you all!
An insert iterator is simply an implementation of an iterator which inserts something in to a collection using standard mechanisms. In the case of back_inserter, the insertion is done by calling the push_back() method on the container. Hence, in order to use back_inserter, the container must implement push_back().
Likewise, with front_inserter the collection mush implement push_front(), which vector does not. Therefore, you can't use front_inserter on a vector.
list and deque both implement push_front, so if you were to use one of those rather than a vector, you could use front_inserter.
No, but what you want is an inserter:
std::set_difference(a1, a1 + 7, a2, a2 + 3, std::inserter(a3, a3.begin()));