I had trouble searching for potential duplicates because I'm not sure what the correct terminology is.
If I have many vectors which are already created, how can I loop through them? To make things simple, suppose I have three vectors of strings named "vec_one", "vec_two", "vec_three".
I want to do something like:
for i in ("vec_one", "vec_two", "vec_three") {
for (vector<string>::const_iterator iter = i.begin(); iter != i.end(); ++iter) {
//do something with the elements ***and I need to access "i"***, that is, the vector name.
}
}
This would be the same as writing three different for loops, but would be more readable and in fact I have more than three in my non-simple application.
Note that because I need to access the vector name (see the comment), I can't just merge them all together and then run one loop.
You can do it with an array:
const vector<string>* varr[] = { &vec_one, &vec_two, &vec_three, &etc };
for (auto vec = begin(varr); vec < end(varr); ++vec)
for (vector<string>::const_iterator iter = begin(**vec); iter != end(**vec); ++iter)
//do something with the elements
You could put the the vectors in a vector<std::pair<std::string, std::vector<...>*>:
std::vector<std::pair<std::string, std::vector<std::string>*> > vectors;
vectors.emplace_back(std::string("vec_one"), &vec_one); //or push_back(std::make_pair(...)) in C++03
vectors.emplace_back(std::string("vec_two"), &vec_two);
vectors.emplace_back(std::string("vec_three"), &vec_three);
for(auto iter = vectors.begin(); iter != vectors.end(); ++iter)//used c++11 auto here for brevity, but that isn't necessary if C++11 is not availible
for(auto vecIter = iter->second->begin(); vecIter != iter->second->end(); ++vecIter)
//get name with iter->first, body here
That way you can get the name easily from the outer iterator.
If you use C++11 you can use std::array instead:
std::array<std::pair<std::string, std::vector<std::string>*>, 3> vectors =
{
std::make_pair(std::string("vec_one"), &vec_one),
std::make_pair(std::string("vec_two"), &vec_two),
std::make_pair(std::string("vec_three"), &vec_three)
};
In C++03 you could use buildin arrays instead, but unless the extra overhead for the vector is a problem for you (unlikely) I don't see a compelling reason to do so. boost::array is also a noteworthy alternative if you can't use C++11
If you do need the absolute optimal performance it might be worthwile to directly use const char* instead of std::string for the names.
Probably the easiest way would be to have your vectors in an array (or a vector-of-vectors if there is a variable number of them).
I guess you'd also want an array of "vector names" to satisfy your second condition.
Related
I have the following problem:
vector<CPerson>toRemove;// has some objects that are contained in m_vObjects
vector<CPerson>m_vObjects;
for (auto it = toRemove.begin(); it != toRemove.end(); ++it)
{
for (auto iter = m_vObjects.begin(); iter != m_vObjects.end(); ++iter)
{
iter = m_vObjects.erase(it);
}
}
What I want to do is delete all the objects from m_vObjects that are contained in toRemove. I have tried a lot of stuff, but nothing worked fine.
Your code is wrong: you cannot use iterator from one container to remove items from the other container. One way to achieve what you want is to use std::set_difference (requires your arrays to be sorted):
vector<CPerson>toRemove;// has some objects that are contained in m_vObjects
vector<CPerson>m_vObjects;
vector<int> diff;
std::set_difference(
m_vObjects.begin(), m_vObjects.end(),
v2.begin(), v2.end(),
std::inserter(diff, diff.begin()));
m_objects = diff;
If it is not desired to sort arrays, then you can use std::remove:
for (const auto& element_to_remove : toRemove) {
std::remove (
m_vObjects.begin (),
m_vObjects.end (),
element_to_remove);
}
1) You cannot use iterator from toRemove in m_vObjects. They are strongly connected to each instance of collection.
2) It is not really a good practice to use loop on iterators while modifying collection: for example you have deleted an object after current interator, but he still thinks that object is there. If I'm correct, it causes undefined behavior.
3) The fix should be check equality of objects, that are pointed by iterators.
if( ( *it == *iter )
{
iter= vObjects.erase( iter );
}
But still, my solution works only if == is overloaded for person, or you have another method that checks equality. And it is very bad for performance. Actually, IMHO, such actions at vectors are always bad for performance, it's better to use set or map if you need to find something. And set has some in-build methods to intersect them.
this is my homework:
Write a function to prints all strings with a length of 3. Your
solution must use a for loop with iterators.
void print3(const set & str)
And this is my code:
void print3(const set<string>& str){
string st;
set<string,less<string>>::iterator iter;
for(iter=str.begin();iter!=str.end();++iter)
{st=*iter;
if(st.length()==3) cout<<st<<' ';
}
}
But I think it's not good. Do someone have a better code? Please, help me to improve it.
-I have another question about iterator
string name[]={"halohg","nui","ght","jiunji"};
set<string> nameSet(name,name+4);
set<string>::iterator iter;
iter=name.begin();
How can I access name[2]="ght" by using iterator?
I tried iter+2 but it has some problems. I think I have to use random access iterator but I don't know how to use it.
Please, help me. Thanks a lot!
Some thoughts on improvement:
You can get rid of string st; and just check if (iter->length() == 3).
Another improvement would be to use a const_iterator instead of an iterator, since you aren't modifying any of the items.
Also, adding less<string> as a template parameter is kind of useless, since that's the default compare functor anyway, so it can be removed.
And lastly, it's generally a good idea to declare your locals with minimal scope (so they don't pollute other scopes or introduce unexpected hiding issues), so usually you want to declare your iter in the for.
So it becomes:
for (set<string>::const_iterator iter = str.begin(); iter != str.end(); ++iter) {
if (iter->length() == 3) cout << *iter << ' ';
}
That's about as good as you can get, given your requirements.
As for your second question, set's iterator is not a random access iterator. It's a (constant) Bidirectional Iterator. You can use std::advance if you wanted, though, and do:
std::set<std::string>::iterator iter;
iter = name.begin();
std::advance(iter, 2);
// iter is now pointing to the second element
Just remember that set sorts its elements.
This question already has answers here:
Why use iterators instead of array indices?
(27 answers)
Closed 10 years ago.
Why should I use iterators?
For example if I have code like this:
for (int i = 0; i < vec.size(); i++)
cout << vec[i];
what would be the advantage of writing
for (vector<int>::iterator it != vec.begin(); it != n.end(); ++it)
cout << *it;
Also, why is writing i < vec.size() and i++ more common in the first example and it != begin() and ++it more common in the second example? What is the difference how you increment it and why not always use an equal sign?
I understand iterators can be useful in C++11 range-based for loops and some STD algorithms, but why should I do it in normal code, since it is more verbose?
Well not all containers have random access so you can do lookup on an index, so to normalize the interface iterators are fairly useful. Consider std::list. It doesn't support random access via a [] operator.
Taking this into account, to work across many heterogeneous types of containers, many STL functions like std::copy take iterators.
The point is that iterators allow you to iterate over anything that supports iterators in a generic fashion.
As for it being more verbose, the extra verbosity isn't horrible (and your example could be slightly improved using auto or using the C++11 range-based for loop) but that's really a stylistic issue.
Lets say we have this code:
typedef std::vector<std::string> strings;
strings strs;
for( strings::const_iterator it = strs.begin(); it != strs.end(); ++it ) {
}
And later for watever reason we decide to switch to std::list. So we just replace typedef and code:
typedef std::list<std::string> strings;
strings strs;
for( strings::const_iterator it = strs.begin(); it != strs.end(); ++it ) {
}
Will work as before. But code with index variable will fail. Imagine what if you need to write a template code.
The tl;dr is that iterators work better in a general case for different kinds of objects (when for example the size() method may be slow).
If you want to read more about it:
Why use iterators instead of array indices?
Iterators.. why use them?
I am new to the C++ language. I have been starting to use vectors, and have noticed that in all of the code I see to iterate though a vector via indices, the first parameter of the for loop is always something based on the vector. In Java I might do something like this with an ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
Is there a reason I don't see this in C++? Is it bad practice?
The reason why you don't see such practice is quite subjective and cannot have a definite answer, because I have seen many of the code which uses your mentioned way rather than iterator style code.
Following can be reasons of people not considering vector.size() way of looping:
Being paranoid about calling size() every time in the loop
condition. However either it's a non-issue or it can be trivially
fixed
Preferring std::for_each() over the for loop itself
Later changing the container from std::vector to other one (e.g.
map, list) will also demand the change of the looping mechanism,
because not every container support size() style of looping
C++11 provides a good facility to move through the containers. That is called "range based for loop" (or "enhanced for loop" in Java).
With little code you can traverse through the full (mandatory!) std::vector:
vector<int> vi;
...
for(int i : vi)
cout << "i = " << i << endl;
The cleanest way of iterating through a vector is via iterators:
for (auto it = begin (vector); it != end (vector); ++it) {
it->doSomething ();
}
or (equivalent to the above)
for (auto & element : vector) {
element.doSomething ();
}
Prior to C++0x, you have to replace auto by the iterator type and use member functions instead of global functions begin and end.
This probably is what you have seen. Compared to the approach you mention, the advantage is that you do not heavily depend on the type of vector. If you change vector to a different "collection-type" class, your code will probably still work. You can, however, do something similar in Java as well. There is not much difference conceptually; C++, however, uses templates to implement this (as compared to generics in Java); hence the approach will work for all types for which begin and end functions are defined, even for non-class types such as static arrays. See here: How does the range-based for work for plain arrays?
Is there any reason I don't see this in C++? Is it bad practice?
No. It is not a bad practice, but the following approach renders your code certain flexibility.
Usually, pre-C++11 the code for iterating over container elements uses iterators, something like:
std::vector<int>::iterator it = vector.begin();
This is because it makes the code more flexible.
All standard library containers support and provide iterators. If at a later point of development you need to switch to another container, then this code does not need to be changed.
Note: Writing code which works with every possible standard library container is not as easy as it might seem to be.
The right way to do that is:
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
it->doSomething();
}
Where T is the type of the class inside the vector. For example if the class was CActivity, just write CActivity instead of T.
This type of method will work on every STL (Not only vectors, which is a bit better).
If you still want to use indexes, the way is:
for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
v[i].doSomething();
}
Using the auto operator really makes it easy to use as one does not have to worry about the data type and the size of the vector or any other data structure
Iterating vector using auto and for loop
vector<int> vec = {1,2,3,4,5}
for(auto itr : vec)
cout << itr << " ";
Output:
1 2 3 4 5
You can also use this method to iterate sets and list. Using auto automatically detects the data type used in the template and lets you use it.
So, even if we had a vector of string or char the same syntax will work just fine
A correct way of iterating over the vector and printing its values is as follows:
#include<vector>
// declare the vector of type int
vector<int> v;
// insert elements in the vector
for (unsigned int i = 0; i < 5; ++i){
v.push_back(i);
}
// print those elements
for (auto it = v.begin(); it != v.end(); ++it){
std::cout << *it << std::endl;
}
But at least in the present case it is nicer to use a range-based for loop:
for (auto x: v) std::cout << x << "\n";
(You may also add & after auto to make x a reference to the elements rather than a copy of them. It is then very similar to the above iterator-based approach, but easier to read and write.)
There's a couple of strong reasons to use iterators, some of which are mentioned here:
Switching containers later doesn't invalidate your code.
i.e., if you go from a std::vector to a std::list, or std::set, you can't use numerical indices to get at your contained value. Using an iterator is still valid.
Runtime catching of invalid iteration
If you modify your container in the middle of your loop, the next time you use your iterator it will throw an invalid iterator exception.
Here is a simpler way to iterate and print values in vector.
for(int x: A) // for integer x in vector A
cout<< x <<" ";
With STL, programmers use iterators for traversing through containers, since iterator is an abstract concept, implemented in all standard containers. For example, std::list has no operator [] at all.
I was surprised nobody mentioned that iterating through an array with an integer index makes it easy for you to write faulty code by subscripting an array with the wrong index. For example, if you have nested loops using i and j as indices, you might incorrectly subscript an array with j rather than i and thus introduce a fault into the program.
In contrast, the other forms listed here, namely the range based for loop, and iterators, are a lot less error prone. The language's semantics and the compiler's type checking mechanism will prevent you from accidentally accessing an array using the wrong index.
don't forget examples with const correctness - can the loop modify the elements. Many examples here do not, and should use cont iterators. Here we assume
class T {
public:
T (double d) : _d { d } {}
void doSomething () const { cout << _d << endl; return; }
void changeSomething () { ++_d; return; }
private:
double _d;
};
vector<T> v;
// ...
for (const auto iter = v.cbegin(); iter != v.cend(); ++iter) {
iter->doSomething();
}
Note also, that with the C++11 notation, the default is to copy the element. Use a reference to avoid this, and/or to allow for original elements to be modified:
vector<T> v;
// ...
for (auto t : v) {
t.changeSomething(); // changes local t, but not element of v
t.doSomething();
}
for (auto& t : v) { // reference avoids copying element
t.changeSomething(); // changes element of v
t.doSomething();
}
for (const auto& t : v) { // reference avoids copying element
t.doSomething(); // element can not be changed
}
//different declaration type
vector<int>v;
vector<int>v2(5,30); //size is 5 and fill up with 30
vector<int>v3={10,20,30};
//From C++11 and onwards
for(auto itr:v2)
cout<<"\n"<<itr;
//(pre c++11)
for(auto itr=v3.begin(); itr !=v3.end(); itr++)
cout<<"\n"<<*itr;
int main()
{
int n;
int input;
vector<int> p1;
vector<int> ::iterator it;
cout << "Enter the number of elements you want to insert" << endl;
cin >> n;
for (int i = 0;i < n;i++)
{
cin >> input;
p1.push_back(input);
}
for(it=p1.begin();it!=p1.end();it++)
{
cout << *it << endl;
}
//Iterating in vector through iterator it
return 0;
}
conventional form of iterator
If you use
std::vector<std::reference_wrapper<std::string>> names{ };
Do not forget, when you use auto in the for loop, to use also get, like this:
for (auto element in : names)
{
element.get()//do something
}
I have two sets and I'm trying to do a union (I get the same error when doing an intersection). Here is the error:
error C3892: 'std::_Tree_const_iterator<_Mytree>::operator *' : you cannot assign to a variable that is const
Code snippet(if I comment out the line with the --> then the code compiles and my work around way of doing the union works fine):
set<Line *>::iterator it;
set<Line *> * newSet = new set<Line *>();
leftLines = pLeft->getSet();
rightLines = pRight->getSet();
-->it = set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), newSet->begin());
for(it = leftLines->begin(); it != leftLines->end(); it++)
{
newSet->insert(*it);
}
for(it = rightLines->begin(); it != rightLines->end(); it++)
{
newSet->insert(*it);
}
it = newSet->begin();
while(it != newSet->end())
{
result->insert(*it);
it++;
}
I'm sure this is something silly but I'm kind of lost. I think that code snippet should be enough but I can provide whatever else is needed. Thanks.
This is C++, not Java [edit: or .NET]. You almost certainly want to replace (for example):
set<Line *> * newSet = new set<Line *>();
with just:
set<Line *> newSet;
...or, better still, probably just:
set<Line> newSet;
Although it's impossible to say for certain based on the code you've posted, there's a pretty fair chance that your left and right shouldn't be dealing in pointers either -- if they're going to do anything of the sort, a reference probably makes more sense (though, as I said, based on just what you've posted, it's impossible to say for sure).
Once you've done that, you run into a minor problem: a "normal" iterator over a set (or multiset, map or multimap) is really a const_iterator. Once you insert something into an associative container, you're not allowed to change it because that could destroy the collection's invariant (being sorted). If you want to change an existing item, you need to delete if from the contain, make the change, and insert the changed object back into the container. In your case, you're just inserting new items, so you want an insert_iterator.
Since you're not planning on modifying either left or right, you might as well treat them as const as well:
std::set_union(left.cbegin(), left.cend(),
right.cbegin(), right.cend(),
std::inserter(newSet, newSet.end()));
If you decide to simulate set_union on your own, you can do something like this:
std::set<Line> newSet(left.cbegin(), left.cend());
std::copy(right.cbegin(), right.cend(), std::inserter(newSet, newSet.end()));
Edit:
Instead of passing around pointers to containers, you normally want to pass around iterators into the containers. For example, to print out the contents, you apparently now have something like:
void print_data(std::vector<Line *> const *data) {
for (int i=0; i<data->size(); i++)
std::cout << *(*data)[i] << "\n";
}
It probably has more formatting and such, but for the moment we'll ignore those details and assume it's this simple. To write the data directly from a container of your choice, you normally want a template that will accept iterators of an arbitrary type:
template <class inIt>
void print_data(inIt begin, inIt end) {
while (begin != end)
std::cout << *begin++ << '\n';
}
We can, however, go a step further than that, and specify the output as an iterator as well:
template <class inIt, class outIt>
void print_data(inIt begin, inIt end, outIt dest) {
while (begin != end) {
*dest++ = *begin++;
*dest++ = '\n';
}
}
You could go one more step, and allow the user to specify the delimiter to be used between the items, instead of always using '\n', but at that point, you'd just be duplicating something what's already in the standard library -- a combination of std::copy and an std::ostream_iterator, which is how you probably want to deal with this in reality:
std::copy(newSet.begin(), newSet.end(),
std::ostream_iterator<Line>(std::cout, "\n"));
Note, however, that as far as the standard library cares, an ostream_iterator is just another iterator. If you're just going to print out the union of left and right, you can skip even creating a set to hold that union, and just print it out directly:
std::set_union(left.cbegin(), left.cend(),
right.cbegin(), right.cend(),
std::ostream_iterator<Line>(std::cout, "\n"));
The fact that an ostream_iterator writes to a file instead of putting things into a normal collection is entirely irrelevant to the standard library. It has a few classes of iterators, and can write output to any iterator that models the correct class.
Now, I may be jumping the gun, so to speak -- maybe need to do other processing on the data before you write it to the console. My point isn't that you necessarily have to write the union directly to standard output, but just that you don't necessarily have to write it to some other collection before you print it out.
set iterators aren't output iterators. Use this:
set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), inserter(*newSet, newSet->begin()));
Also, why're you filling newSet? Leave it as is after the union/intersection or the union/intersection will be pointless.
set<Line *>::iterator it;
set<Line *> newSet; // No need to `new` this
leftLines = pLeft->getSet();
rightLines = pRight->getSet();
set_union(leftLines->begin(),leftLines->end(),rightLines->begin(), rightLines->end(), inserter(newSet, newSet.begin()));
// Assuming you really need the below code - you could likely just make an inserter directly on `result` instead of the copying.
it = newSet.begin();
while(it != newSet.end())
{
result->insert(*it);
it++;
}