How can I use a overloaded const_iterator inside a class? - c++

I'm doing a University project in C++, whose purpose is to learn how to use the diferent STL containers and their iterators.
In my program, I have a class with a set:
class ConjuntoDeLetras{
private:
set<Letra> letras;
public:
ConjuntoDeLetras();
···
};
Inside the class, I have two nested class, iterator and const_iterator. (I don't know if it's the best way to make class iterators, but the teacher tell us that we need to do at this way):
class iterator{
private:
set<Letra>::iterator it;
public:
iterator();
Letra operator*();
ConjuntoDeLetras::iterator& operator++();
ConjuntoDeLetras::iterator& operator+=(int num);
bool operator==(const ConjuntoDeLetras::iterator &i);
bool operator!=(const ConjuntoDeLetras::iterator &i);
friend class ConjuntoDeLetras;
};
class const_iterator{
private:
set<Letra>::const_iterator it;
public:
const_iterator();
Letra operator*();
ConjuntoDeLetras::const_iterator& operator++();
ConjuntoDeLetras::const_iterator& operator+=(int num);
bool operator==(const ConjuntoDeLetras::const_iterator &i);
bool operator!=(const ConjuntoDeLetras::const_iterator &i);
friend class ConjuntoDeLetras;
};
Both iterator class methods works well. In ConjuntoDeLetras class we have the begin and end methods:
ConjuntoDeLetras::iterator begin();
ConjuntoDeLetras::const_iterator begin() const;
ConjuntoDeLetras::iterator end();
ConjuntoDeLetras::const_iterator end() const;
The problem is here. When I'm going to use the const_iterator we have problems:
ConjuntoDeLetras::const_iterator itL;
for(itL=L.begin(); itL!=L.end(); ++itL){
CantidadLetras aux;
aux.frecuenciaAbsoluta = 0;
aux.frecuenciaRelativa = 0;
aux.letra = (*itL).getLetra();
salida.push_back(aux);
}
When I execute this code the compiler says that I don't have operator= for const_iterator to iterator. I know the reason of the problem, it's because the object L is a not const variable and uses the normal iterator begin() and end(). I thought about removing the final const of the functions but I can't overload the function only with the return type. I don't know what is the best solution. Here is the compiler error:
error: no match for ‘operator=’ (operand types are ‘ConjuntoDeLetras::const_iterator’ and ‘ConjuntoDeLetras::iterator’)
for(itL=L.begin(); itL!=L.end(); ++itL){
^
And the same error with the end().

You are missing the other ++ operator and the required typedefs to be an iterator.
Plus, you need =default copy/move/assignments.
Finally you need a converting constructor from your iterator to your const_iterator.
Optionaly add two const_iterator::operator=(iterator) overloads (copy assign and move assign), plus const_iterator::operator==(iterator const&) and iterator==const_iterator similarly.

Related

C++: copy constructor for iterators

I am currently in the process of refreshing my knowledge of the language, and have found the following example here defining an iterator class. Am I mistaken in saying that the way the copy constructor in this iterator is defined in such a way that it will only create a shallow copy or am I missing something?
Wouldn't this create problems when used given that the class attribute is an int pointer?
Thanks.
// std::iterator example
#include <iostream> // std::cout
#include <iterator> // std::iterator, std::input_iterator_tag
class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() {++p;return *this;}
MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
bool operator==(const MyIterator& rhs) const {return p==rhs.p;}
bool operator!=(const MyIterator& rhs) const {return p!=rhs.p;}
int& operator*() {return *p;}
};
The concept of "deep" copying only applies to objects owning another object through some kind of reference (e.g. a pointer). The iterator uses its value (the pointer) in an non-owning fashion, and so copying the value of that pointer is enough to maintain the semantics of the class.
In fact (as pointed by Caleth in the comments), in this case the compiler would generate precisely this copy-ctor by default, so you don't even have to write it.
If your copy-ctor actually copied the data being pointed to, it wouldn't be a very good iterator, because you'd then lose the access to the object you wanted to iterate.

How can I define a STL compatible input iterator without interhitance?

I am trying to learn C++ and would like to understand iterators better. To that end, I tried to write an STL compatible iterator for a custom class (called StrBlob which holds strings, defined in the wonderful C++ Primer). Eventually, the following iterator definition worked:
class StrBlob::iterator
: public std::iterator<std::input_iterator_tag, // iterator_category
std::string, // value_type
std::string, // difference_type
const std::string*, // pointer
std::string // reference
> {
public:
iterator() : curr(0) {}
iterator(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
iterator& operator++(); // prefix operators
iterator& operator--();
iterator operator++(int); // postfix operators
iterator operator--(int);
std::string& operator*() const;
std::string* operator->() const;
bool operator==(const iterator& b) const { return curr == b.curr; }
bool operator!=(const iterator& b) const { return curr != b.curr; }
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t,
const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
Initially I tried for a long time in vain to design the iterator with the right type definitions myself. This didn't work, so I simply provided an inheritance to std::iterator as mentioned in cppreference.
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
(I tried to understand Stackoverflow answers to related questions about iterator design but they were a bit to abstract for me.)
Your code is bugged, std::string is not a suitable difference type (difference_type is the difference between two iterators so normally its std::ptrdiff_t), nor is std::string a suitable reference type (should be std::string&).
Here's the correct typedefs, you can use these instead of inheritance.
typedef std::input_iterator_tag iterator_category;
typedef std::string value_type;
typedef std::ptrdiff_t difference_type;
typedef std::string* pointer;
typedef std::string& reference;
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
Yes, std::iterator (deprecated in C++17), only provides typedef.
So you can add those typedef yourself instead of using inheritance.
class StrBlob::iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = std::string;
using difference_type = std::string;// should be `std::ptrdiff_t`
using pointer = const std::string*; // const should probably be dropped, unless you do const_iterator
using reference = std::string; // should be std::string&
// ...
};

C++, having issues using const_iterator to make operator=

There is a compilation error which occurs when I write the following:
const_iterator it = cp.begin();
const_iterator is my own class for const iterator.
cp is an object of a class ConjuntoPreguntas (see below).
The error is:
mainprueba.cpp:30:6: error: no match for ‘operator=’ (operand types are ‘ConjuntoPreguntas::const_iterator’ and ‘ConjuntoPreguntas::iterator’)
cit = CP.begin();
^
mainprueba.cpp:30:6: note: candidate is:
In file included from mainprueba.cpp:2:0:
conjuntopreguntas.h:258:21: note: ConjuntoPreguntas::const_iterator& ConjuntoPreguntas::const_iterator::operator=(const ConjuntoPreguntas::const_iterator&)
const_iterator& operator=(const const_iterator& cit){
^
conjuntopreguntas.h:258:21: note: no known conversion for argument 1 from ‘ConjuntoPreguntas::iterator’ to ‘const ConjuntoPreguntas::const_iterator&’
The code:
class ConjuntoPreguntas{
private:
map<int,Pregunta> preguntas;
public:
class const_iterator;
class iterator{
private:
map<int,Pregunta>::iterator it;
public:
iterator & operator++(){
++it;
}
iterator & operator--(){
--it;
}
pair<const int,Pregunta> &operator *(){
return *it;
}
bool operator ==(const iterator &i){
return i.it==it;
}
bool operator !=(const iterator &i){
return i.it!=it;
}
friend class ConjuntoPreguntas;
friend class const_iterator;
};
class const_iterator{
private:
map<int,Pregunta>::iterator it;
public:
const_iterator(){
}
const_iterator & operator++(){
++it;
}
const_iterator & operator--(){
--it;
}
pair<const int,Pregunta> &operator *(){
return *it;
}
bool operator ==(const const_iterator &i){
return i.it==it;
}
bool operator !=(const const_iterator &i){
return i.it!=it;
}
const_iterator& operator=(const const_iterator& cit){
}
friend class ConjuntoPreguntas;
};
iterator begin(){
iterator i;
i.it=preguntas.begin();
return i;
}
iterator end(){
iterator i;
i.it=preguntas.end();
return i;
}
/* other code, irrelevant to the problem */
};
If anyone can help me, I will be very grateful.
Your most immediate problem is because you don't have a const version of begin:
const_iterator begin() const
{
const_iterator i;
i.it = preguntas.begin();
return i;
}
But then also your const_iterator class uses map's iterator which is going to result in another problem.
If you're writing a container-looking class, there's nothing for it but to write const-correct iterator and const_iterator classes and provide the const-correct members.
If however you are NOT writing a container, you may not want to do this. The best case is to provide a container-agnostic interface to the class, where for example you provide meaningful names rather than direct container access. Alternately provide const-only access via the map const_iterator (don't write your own iterator class).
If you want to fully encapsulate std container inside your class (daunting and usually unneccessary task) you need to make sure you also define all conversions. In particlar, standard container iterators have a constructor which creates const_iterator from iterator (but not the other way around!). You will have to create it yourselfa as well.
However, a much better design choice would be to simply expose the member map variable.

Custom iterator of 2D vector [duplicate]

I am a bit new at c++, so the question may be without any meaning, so, sorry about that in advance.
So, i have a class of hashtable, my hashtable is vector of vectors, it means that i used
std::vector<std::vector<std::string> > htable;
My task is - create own iterator with operation ++, --, -> and *. I wrote this
class hashTable
{
public:
hashTable(int size);
~hashTable();
void add(std::string s);
bool inHash(std::string s);
void deletestr(std::string s);
void printall();
int maxcoll();
private:
std::vector<std::vector<std::string> > htable;
std::vector<std::vector<std::string> > newhtable;
int hfunc(std::string s);
void reallocate();
int M;
int teksize;
int issame(std::string a, std::string b);
class myiterator{
myiterator();
myiterator operator*();
myiterator operator++();
myiterator operator--();
myiterator operator->();
};
myiterator begin() {return htable.begin()}
myiterator end() {return htable.end()}
};
I think, that i understand what the interator is, but now i suppose that i was wrong, so when i try to compile this, there is a mistake in lines
myiterator begin() {return htable.begin()}
myiterator end() {return htable.end()}
/Users/ratkke/Programms/c++/mipt/tasks/#5/myhash.cpp:37:29: error: no viable conversion from 'iterator' (aka '__wrap_iter') to 'hashTable::myiterator'
myiterator begin() {return htable.begin()}
^~~~~~~~~~~~~~
/Users/ratkke/Programms/c++/mipt/tasks/#5/myhash.cpp:29:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'iterator' (aka '__wrap_iter') to 'const hashTable::myiterator &' for 1st argument
class myiterator{
And i don't know why. Also, can you tell me about realisation of iterator(or just link to article) for iterator for vectors, because i can't understand how i must implement all this operators.
Thank you in advance.
You need to implement your myiterator. There are lots of way to do that, but at very least you must add something to myiterator. For instance
class myiterator{
public:
myiterator();
myiterator(std::vector<std::vector<std::string> >& v, int ii, int jj) :
vec(v), i(ii), j(jj) {}
std::string operator*();
myiterator& operator++(); // prefix operator
myiterator operator++(int); // postfix operator
myiterator& operator--(); // prefix operator
myiterator operator--(int); // postfix operator
std::string* operator->();
private:
std::vector<std::vector<std::string> >& vec; // the vector we are iterating over
int i; // the position in the vector (first dimension)
int j; // the position in the vector (second dimension)
};
myiterator begin() {return myiterator(htable, 0, 0);}
Lots of other ways to do it, and the above may not be exactly what you want, but hopefully this gives you the idea, myiterator must have some data members that hold the vector being iterated over and the position reached so far.
Also note the return type of operator* is wrong, it should (presumably) be std::string. Some of the other operators don't look right either, I've put what I think is correct in the code.

Creating own iterator for double vectors

I am a bit new at c++, so the question may be without any meaning, so, sorry about that in advance.
So, i have a class of hashtable, my hashtable is vector of vectors, it means that i used
std::vector<std::vector<std::string> > htable;
My task is - create own iterator with operation ++, --, -> and *. I wrote this
class hashTable
{
public:
hashTable(int size);
~hashTable();
void add(std::string s);
bool inHash(std::string s);
void deletestr(std::string s);
void printall();
int maxcoll();
private:
std::vector<std::vector<std::string> > htable;
std::vector<std::vector<std::string> > newhtable;
int hfunc(std::string s);
void reallocate();
int M;
int teksize;
int issame(std::string a, std::string b);
class myiterator{
myiterator();
myiterator operator*();
myiterator operator++();
myiterator operator--();
myiterator operator->();
};
myiterator begin() {return htable.begin()}
myiterator end() {return htable.end()}
};
I think, that i understand what the interator is, but now i suppose that i was wrong, so when i try to compile this, there is a mistake in lines
myiterator begin() {return htable.begin()}
myiterator end() {return htable.end()}
/Users/ratkke/Programms/c++/mipt/tasks/#5/myhash.cpp:37:29: error: no viable conversion from 'iterator' (aka '__wrap_iter') to 'hashTable::myiterator'
myiterator begin() {return htable.begin()}
^~~~~~~~~~~~~~
/Users/ratkke/Programms/c++/mipt/tasks/#5/myhash.cpp:29:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'iterator' (aka '__wrap_iter') to 'const hashTable::myiterator &' for 1st argument
class myiterator{
And i don't know why. Also, can you tell me about realisation of iterator(or just link to article) for iterator for vectors, because i can't understand how i must implement all this operators.
Thank you in advance.
You need to implement your myiterator. There are lots of way to do that, but at very least you must add something to myiterator. For instance
class myiterator{
public:
myiterator();
myiterator(std::vector<std::vector<std::string> >& v, int ii, int jj) :
vec(v), i(ii), j(jj) {}
std::string operator*();
myiterator& operator++(); // prefix operator
myiterator operator++(int); // postfix operator
myiterator& operator--(); // prefix operator
myiterator operator--(int); // postfix operator
std::string* operator->();
private:
std::vector<std::vector<std::string> >& vec; // the vector we are iterating over
int i; // the position in the vector (first dimension)
int j; // the position in the vector (second dimension)
};
myiterator begin() {return myiterator(htable, 0, 0);}
Lots of other ways to do it, and the above may not be exactly what you want, but hopefully this gives you the idea, myiterator must have some data members that hold the vector being iterated over and the position reached so far.
Also note the return type of operator* is wrong, it should (presumably) be std::string. Some of the other operators don't look right either, I've put what I think is correct in the code.