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

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.

Related

How to convert an iterator-like that has a `next()` methode to a regular `begin`/`end` iterator pair?

In the codebase I inherited, there is a class that look like an iterator (this isn’t the exact code, but the logic is similar).
template <class T>
struct IteratorLike {
T* next() &; // either return a pointer to a valid value or nullptr
};
The way you use it is very similar to the way you use Rust iterators:
IteratorLike<...> it = ...;
while(auto* item = it.next()) {
do_something(*item);
}
How do I convert it to make it compatible with C++ range-based for loop, algorithms, or range-v3? I’m using C++14 (gcc5.5 to be more precise), so I can’t have a sentinel type that is different from the type of the iterator itself.
So far it seems that the easiest way is to store both the iterator and the next value in my wrapper:
template <class T>
class MyIterator {
private:
IteratorLike<T> m_iter;
T* m_value;
public:
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
assert(m_value && "trying to read past the end of the iterator");
return *m_value;
}
pointer operator->() {
// I’m not sure the assert is needed here
assert(m_value && "trying to read past the end of the iterator");
return m_value;
}
// Prefix increment
MyIterator& operator++() {
m_value = m_iter.next();
return *this;
}
// Postfix increment
MyIterator operator++(int) {
MyIterator tmp = *this;
++(*this);
return tmp;
}
// used by `my_collection.begin()`
explicit MyIterator(IteratorLike<T> iter)
: m_iter{m_iter}
, m_value{this->self.next()}
{}
// missing operator == and operator != as well as the constructor
// used `my_collection.end()
};
However, I fail to understand what my_collection.end() should return (EDIT: I just check, I can’t default-initialize m_iter), nor how to have meaningful comparison operators.
Note: I’m basically trying to do the exact reverse of this.
Since IteratorLike isn't default constructible, but is obviously copy constructible, you could use the instance you have to construct your end() iterator too. Example:
// used by `my_collection.begin()`
explicit MyIterator(const IteratorLike<T>& iter) :
m_iter{iter},
m_value{m_iter.next()}
{}
// used by `my_collection.end()`
MyIterator(const IteratorLike<T>& iter, std::nullptr_t) :
m_iter{iter},
m_value{nullptr}
{}
bool operator!=(const MyIterator& rhs) const {
return m_value != rhs.m_value;
}
Then in my_collection:
template<typename T>
class my_collection {
public:
MyIterator<T> begin() { return MyIterator<T>{itlike}; }
MyIterator<T> end() { return {itlike, nullptr}; }
private:
IteratorLike<T> itlike;
};

How can I use a overloaded const_iterator inside a class?

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.

constructors and conversions of classes

I have 2 classes:
class iterator {
Node<K,V>* n;
public:
iterator(){
std::cout<<"new empty iterator"<<std::endl;
};
iterator(const iterator& iter):n(iter.n){
std::cout<<"copy iterator"<<std::endl;
}
explicit iterator(Node<K,V>* nodePtr):n(nodePtr) {
std::cout<<"create iterator with Node Ptr"<<std::endl;
}
iterator& operator=(const iterator& iter);
void operator++();
Node<K,V>& operator*();
bool operator!=(const iterator& iter);
K& operator[](const Key& k)const;
V& operator[](const Val& v)const;
Node<K,V>* getNodePtr()const{
std::cout<<"get Node Ptr"<<std::endl;
return n;
}
friend class Map;
};
class const_iterator :public iterator {
const Node<K,V>* n;
public:
const_iterator(){
std::cout<<"new empty const_iterator"<<std::endl;
};
const_iterator(const iterator& iter):
n(iter.getNodePtr()){
std::cout<<"convert"<<std::endl;
}
const_iterator(const const_iterator& iter):n(iter.n){}
explicit const_iterator(const Node<K,V>* node):n(node){}
const_iterator& operator=(const const_iterator& iter){
n = iter.n;
return *this;
}
const Node<K,V>& operator*(){
return *n;
}
friend class Map;
};
iterator begin()const{
return iterator(firstKey);
}
iterator end()const{
return iterator(dummyKey);
}
I want it to make an automatic conversion between the 2 classes using:
const_iterator(const iterator& iter):
n(iter.getNodePtr()){
std::cout<<"convert"<<std::endl;
}
to do something like this for example:
Map<int,int>::const_iterator it = m.begin();
now, the thing is, that this constructor calls for some reason iterator(); in the first class and I can't figure out why.
I know begin() and end() have some standart versions, but I can't use any of it here. I also can't make Map to be const, or write a conversion function.
any help?
You've made iterator a base class of const_iterator, so an iterator constructor will (and should) be called as part of constructing a const_iterator.
If you will define data storage as
const Map<int, int> data;
instead of
Map<int, int> data;
const version of begin() and end() will be invoked.
Also, C++11 standard containers add cbegin() and cend() for that purpose. You could re-difine it as follows:
const_iterator cbegin()const{
return const_iterator(firstKey);
}
const_iterator cend()const{
return const_iterator(dummyKey);
}

How to call const_iterator for non const class? [duplicate]

This question already has answers here:
How to use two functions, one returning iterator, the other returning const_iterator
(2 answers)
Closed 9 years ago.
I read some other threads related to this issue but non offered a solution for my problem. I hope you guys can give me ideas or advices.
I'm trying to implement this class named Map. it should contain 2 iterators - iterator and const_iterator.
I have them implemented - iterator inherits from const_iterator, and in the Map class I have the following functions:
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
we were given an example file to see what is required to implement.
in there, there is the following code:
Map<std::string,int> msi;
...
// print map
for(Map<std::string,int>::const_iterator it = msi.begin(); it != msi.end(); ++it) {
// more stuff here
}
since msi is a non-const Map instance, msi.begin() calls to iterator begin() and not const_iterator begin() const, resulting in unintended behaviour.
Assuming the example file is okay, how do I make it so msi.begin() calls the correct const_iterator function? (considering it, the iterator, is of type const_iterator).
EDIT: regarding the talk about the auto-conversion, i decided to add my iterator classes, please point out my mistake.
class Map {
//...
public:
class const_iterator {
private:
Node* currNode;
public:
const_iterator(Node* cur_node = NULL) : currNode(cur_node) {}
const_iterator& operator++() {
currNode = currNode->next;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
++(*this);
return old;
}
bool operator!=(const_iterator const& curr) {
return !(*this == curr);
}
string operator*() {
// this might cause memory leak
string toString(this->currNode->key);
std::stringstream s;
int tmp = this->currNode->value;
s << tmp;
string secondString(s.str());
toString = toString + ":" + secondString;
return toString;
}
bool operator==(const_iterator const& curr) {
return this->currNode == curr.currNode;
}
void operator=(const_iterator target) {
this = target;
}
//void operator=(Node* target) {
// this->currNode = target;
//}
};
class iterator : public const_iterator {
private:
Node* currNode;
public:
iterator(Node* cur_node = NULL) : currNode(cur_node) {}
iterator& operator++() {
currNode = currNode->next;
return *this;
}
iterator operator++(int) {
iterator old = *this;
++(*this);
return old;
}
bool operator==(iterator const& curr) {
return *this == curr;
}
bool operator!=(iterator const& curr) {
return !(*this == curr);
}
string operator*() {
// this might cause memory leak
string toString(this->currNode->key);
std::stringstream s;
int tmp = this->currNode->value;
s << tmp;
string secondString(s.str());
toString = toString + ":" + secondString;
return toString;
}
void operator=(iterator target) {
this = target;
}
};
//..
}
C++11 standard containers add cbegin and cend for that purpose. Lacking that, you can obviously always cast your object to const& explicitly to get a const view on the object.
More fundamentally, however, there’s no reason why your iterator shouldn’t support an automatic conversion to const_iterator. Like that, you’d not need to change the client code at all. In fact, your code should already support this if, as you’ve said, iterator inherits from const_iterator.
However, the code you’ve posted contains several errors. Firstly, operator= is wrong, and you should have received an error for it. The corrected version is:
void operator=(const_iterator target) {
currNode = target.currNode;
}
More importantly, your inheritance makes no sense. True, you do inherit iterator from const_iterator but your code pretends that this never happened – iterator completely reimplements its parent class and doesn’t relate to it in any way.
iterator should rather look something like this:
class iterator : public const_iterator {
public:
iterator(Node* cur_node = NULL) : const_iterator(cur_node) {}
};
This requires of course that currNode is declared protected in const_iterator. That class is also completely useless (but so is yours, at the moment) since it doesn’t add any functionality to the const_iterator class. You need to implement an operator* which allows modifying its value. Your current code fundamentally doesn’t allow this since it returns a newly created string rather than (something akin to) a reference to a map value.
Furthermore, it’s unclear how the const_iterator class gets ahold of a non-const Node pointer in the first place. That shouldn’t be possible: after all, it gets the pointer from a const Map.
If you will define msi
const Map<std::string,int> msi;
instead of
Map<std::string,int> msi;
const version of begin() and end() will be invoked

Vector iterator override operator ->

I have for homework to make my own abstract class Vector. This vector should have iterator. I make iterator in public part of the Vector. This is my code for iterator:
class iterator {
friend class Vector;
Vector* v_;
int position_;
iterator(Vector* v,int position)
: v_(v),
position_(position)
{}
public:
iterator()
: v_(0),
position_(0)
{}
iterator& operator++() {// pre
position_++;
return *this;
}
iterator operator++(int) {//post
iterator res=*this;
position_++;
return res;
}
T& operator*() {
return v_->buffer_[position_];
}
T* operator->() {
return &buffer_[position_];
}
bool operator==(const iterator& other) const {
return position_ == other.position_;
}
bool operator!=(const iterator& other) const {
return !operator==(other);
}
};
My question is if the operator -> is correct defined.
Thanks
I believe you're really wanting a slightly modified definition that gets the value at the current position of the vector, i.e., return &((*v_)[position_]); if you've overloaded the operator[] of your vector class. Otherwise in order to get access to the buffer_ of your vector, you have to dereference the vector first in order to get to the buffer, i.e., return &(v_->buffer[position_]);