c++ iterator confusion - c++

I have a vector<list<customClass> >
I have an iterator vector<list<customClass> >::const_iterator x
When I try to access an member of customClass like so:
x[0]->somefunc(), I get errors of a non-pointer type/not found.

Here's a complete working snippet. To answer your question, the line with the comment [1] shows how to dereference the const_iterator, while comment [2] shows how to dereference using the operator [].
#include <vector>
#include <list>
#include <iostream>
class Foo
{
public:
void hello() const
{
std::cout << "hello - type any key to continue\n";
getchar();
}
void func( std::vector<std::list<Foo> > const& vec )
{
std::vector<std::list<Foo> >::const_iterator qVec = vec.begin();
qVec->front().hello(); // [1] dereference const_iterator
}
};
int main(int argc, char* argv[])
{
std::list<Foo> list;
Foo foo;
list.push_front(foo);
std::vector<std::list<Foo> > vec;
vec.push_back(list);
foo.func( vec );
vec[0].front().hello(); // [2] dereference vector using []
}

The iterator dereferences to a list. If you want to access an object in that list, then you will have to use the list methods to do so. However, since stl lists don't overload the index operator, that won't be a valid option.
This will let you call somefunc on the first element in the list:
(*x).front().somefunc();
On the other hand, if you want an iterator for your list, you can do something like this:
list<customClass>::const_iterator listIterator = (*x).begin();
listIterator->somefunc();

iterator class does not provide operator[] hence you can not use it like that . You should use it as x->somefunc()

x is an iterator, which acts like a pointer - it points to a list. So you can only use functions which are members of std::list.

The const iterator will dereference to a list<customClass> object not a pointer to that list. You'd have to access the index in that list for the class...
Ignoring error checking:
(*x)[0].somefunc()

if you want direct access the list and vector class already implement the [] operator, so just access it directly: vectorObj[x].someFunc();
iterator are for going through the list (to iterate it like the name suggests), use it for that.

Related

C++ Iterator for std::set or std::vector

Let's say I have this:
struct HoldStuff {
std::vector<StuffItem> items;
std::set<StuffItem, StuffItemComparator> sorted_items;
}
Now, during a refactor, I may have stuff in items or I may have it in sorted_items, but regardless I want to do the same thing with each item. I want to do something like this:
HoldStuff holder; // assume it was filled earlier
auto iter = holder.items.empty() ? holder.sorted_items.begin() :
holder.items.begin();
auto iter_end = holder.items.empty() ? holder.sorted_items.end() :
holder.items.end();
for (; iter != iter_end; ++iter) {
auto& item = *iter;
// Do stuff
}
When I go to compile this, I get errors complaining about incompatible operand types. Surely this is possible, no?
You have two options:
use type-erasure to get a runtime polymorphism on the iterator (any_range or any_iterator)
delegate do_stuff to a function template that takes any kind of iterator
Here is an illustration with code:
#include <vector>
#include <set>
#include <iostream>
#include <boost/range/any_range.hpp>
template<typename Iterator>
void do_stuff(Iterator begin, Iterator end) {}
int main()
{
std::vector<int> items;
std::set<int> sorted_items;
// first option
typedef boost::any_range<int, boost::forward_traversal_tag, int&, std::ptrdiff_t> my_any_range;
my_any_range r;
if(items.empty())
r = my_any_range(sorted_items);
else
r = my_any_range(items);
for (auto& x : r) {
std::cout << x << " ";
}
// second option
// this could also be a lambda and std::for_each
if(items.empty())
do_stuff(sorted_items.begin(), sorted_items.end());
else
do_stuff(items.begin(), items.end());
return 0;
}
Both sides of the ternary operator need to have the same type. In your case, they are different - std::vector<>::iterator and std::set<> iterator. A suitable solution seems to be some sort of a an iterator wrapper, which returns one or another depending on the initial condition.
The errors are correct: auto keyword works during compilation. In an easy way, it just deduces the type of assignment and uses this real type. But decision if it's vector's iterator or set's is made in runtime. So type can not be deduced.
As SergeyA said, I'm wrong here, compiler fail on ?: operator, before auto. But the reason is still the same - it has no idea which type to use for the result.
You should probably use some more generic iterator type + polymorphism, or you can make this function parameterized on type , where T is an iterator type. I would prefer to do it this way:
template<class T> do_stuff(T &c) { for (auto &el : c) { /*Whatever*/ } }
...
if (!items.empty()) {
do_stuff(items);
} else if (!sorted_items.empty()) {
do_stuff(sorted_items);
}
P.S.: It's a conception, I didn't test the code.
auto means the compiler will deduce the type of what follows, at compilation time.
The return type of the ternary conditional operator in this case is what follows the question mark, so it is std::set<StuffItem, StuffItemComparator>::iterator and the compiler tries to cast what follows the column (std::vector<StuffItem>::iterator) to this incompatible type, hence the compiler error.
What you can do is make your item processing code generic, like so:
auto doStuff = [] (StuffItem& item) {
// do stuff with item...
};
if( holder.items.size() )
for_each( holder.items.begin(), holder.items.end(), doStuff );
else if( holder.sorted_items.size() )
for_each( holder.sorted_items.begin(), holder.sorted_items.end(), doStuff );

Modifying elements in a STL List - C++

I am attempting to construct a binary search tree using a generalized list in C++.
class Element
{
private:
list<Element*> _children;
char* _name;
// and other data members/methods...
}
As you can see, I have a class "Element" and it has a list "_children" of Element pointers.
I am trying to access these children so that I may add children to them and so forth...
However, I cannot modify these values with my current method of using a "const_iterator" and my reason for doing that is that the "begin()" method of _children returns a const_iterator.
Someone help? Thank you :)
UPDATE: Thank you all so much... It turns out, I mistakenly had a method return a const reference of the _children data member.
const list<Element*>& getChildren();// return [_children]
I just deleted const and it works perfect now. Thank you! :D
The begin function will return a const_iterator if the list is const. So for the _children list you should be able to just get the standard iterator to let you perform non-const operations on it:
list<Element*>::iterator it = _children.begin();
This however won't work if your passing off a const reference to the list and then trying to get the non-const iterator from that. Something like this would not be allowed:
void doSomething( const list<Element*>& l )
{
list<Element*>::iterator it = l.begin();
}
You would need to instead pass a non-const reference to the list.
The other case where this would be not allowed is in a const function, i.e.
void doSomething() const
{
list<Element*>::iterator it = _children.begin();
}
But would need to see more of your code to confirm if you're doing this or not.
If you want to use _children as an array, how about trying std::vector class instead of std::list?
Here's usage.
#include <iostream>
#include <vector>
int main(void) {
std::vector<int> list;
list.push_back(1);
list.push_back(2);
list.push_back(3);
for (int i = 0; i < list.capacity();++i){
std::cout << list[i] << std::endl;
}
return 0;
}

C++, Objects on vector

I wanna store objects on vectors. But I do not know why it does not work.
‪#‎include‬ <iostream>
#include <vector>
using namespace std;
I have a Persona class in the Persona.h file. And it only has two method:
The default constructor and a method called mensaje(), both are public and it does not have any private member.
#include "Persona.h"
int main()
{
vector<Persona> personas;
Persona persona1;
Persona persona2;
personas.push_back(persona1);
personas.push_back(persona2);
vector<Persona>::const_iterator p;
for(p = personas.begin(); p <= personas.end(); p++) {
Here is where I get the error message
p.mensaje();
}
}
I think problem is the way that I am trying to call 'p'.
Is right that I try to use const_iterator instead of any other type?
p is iterator not object itself, you need to dereference it:
(*p).mensaje();
OR
p->mensaje();
And
update:
for(p = personas.begin(); p <= personas.end(); p++) {
to:
for(p = personas.begin(); p != personas.end(); p++) {
^^^^^^
You are trying to call a non-const method on a const object (the object referenced by a const iterator). Since the mensaje() method does not modify the object, it should be declared const, like so:
void Persona::mensaje() const;
After you make this change, you should be able to call the method on the const object (reference) returned from the const iterator.
(...in addition to the other syntax errors mentioned in other answers.)

Get Element Position within std::vector

How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.

Customize an iterator for a wrapped list

I've got a class, memberlist, that contains a std::list of class memberinfo. These represents the peers on a network.
I use the class to add some functionality to the list.
I want to expose some iterators (begin and end) so that outside code can loop through my internal list and read their data. However, I want to have two ways of doing this - one that includes an element for the localhost, and one that doesn't.
What's a good way to do this?
I could put the local node first, then have like begin(showlocal=false) just give the second element instead of the first. Or someone suggested storing a pair of with the bool saying if it's local or not.
Any suggestions on a good way to do this? I'm not too great on advanced STL stuff yet.
Personally I would approach this in a different way and have your memberinfo have a way of telling you if it's local or not.
That way you're not specialising your collection class due to a specialisation of the contained objects. In fact you could just use a standard std::list<memberinfo>.
E.g.
class memberinfo
{
bool IsLocal( ) const;
}
Then you would choose whether you're interested in local members or not while you're iterating through the contained objects.
E.g.
std::list<memberinfo>::iterator it;
std::list<memberinfo> list;
for ( it = list.begin() ; it != list.end() ; it++ )
{
if ( it->IsLocal() )
{
// blah blah blah
}
else
{
// dum dee dum dee
}
}
As I said in comment to your question, I think your first solution is reasonable. However, I'm not sure that giving a parameter to begin is the best approach for discriminating the two cases. The major problem with this is that you cannot use your full collection (including the localhost member) as a range, meaning that you cannot use Boost.Range algorithms or the C++11 range-based for loop.
A simple solution would be to have two different member functions returning the appropriate range, as a pair of iterators. Boost.Range provides a sub_range class, which seems rather appropriate (you want to return a sub-range of the list of members). Here is a sample code using this approach:
#include <boost/range.hpp>
#include <iostream>
#include <string>
#include <vector>
struct MemberInfo
{
std::string name;
};
class MemberList
{
public:
typedef std::vector<MemberInfo>::iterator iterator;
typedef std::vector<MemberInfo>::const_iterator const_iterator;
MemberList()
: members_{MemberInfo{"local"}, MemberInfo{"foo"}, MemberInfo{"bar"}}
{}
boost::sub_range<std::vector<MemberInfo>> all() // includes localhost
{
return boost::sub_range<std::vector<MemberInfo>>(
members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo> const> all() const
{
return boost::sub_range<std::vector<MemberInfo> const>(
members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo>> some() // excludes localhost
{
return boost::sub_range<std::vector<MemberInfo>>(
++members_.begin(), members_.end());
}
boost::sub_range<std::vector<MemberInfo> const> some() const
{
return boost::sub_range<std::vector<MemberInfo> const>(
++members_.begin(), members_.end());
}
private:
std::vector<MemberInfo> members_;
};
Now, you can use either all() or some() depending on whether you want to include local or not, and both can be used as ranges:
int main()
{
MemberList ml;
for (MemberInfo mi : ml.all()) { std::cout << mi.name << '\n'; }
for (MemberInfo mi : ml.some()) { std::cout << mi.name << '\n'; }
}
And of course, you can still use iterators as usual:
std::find_if(ml.all().begin(), ml.all().end(), ...);
If you don't want to leak the fact that your members are stored in a std::vector, you can use any_range, which erases the underlying iterator type.