I am implementing an iterator for a Queue data type, but the iterator is being initialized as a const inside of the class implementation for some reason. I cannot figure out why the constructor is causing the iterator to return itself as a const.
Any feedback as to what the intricasies of the C++ language that may be causing my problem could be, would be very helpful.
The Error I am receiving from Eclipse which seems to be coming from my begin() method is:
../src/linked_queue.hpp:315:35: error: invalid conversion from
'const ics::LinkedQueue<int>*' to 'ics::LinkedQueue<int>*' [-fpermissive]
Interface:
#ifndef LINKED_QUEUE_HPP_
#define LINKED_QUEUE_HPP_
#include <string>
#include <iostream>
#include <sstream>
#include <initializer_list>
#include "ics_exceptions.hpp"
namespace ics {
template<class T> class LinkedQueue {
public:
//Destructor/Constructors
~LinkedQueue();
LinkedQueue ();
LinkedQueue (const LinkedQueue<T>& to_copy);
explicit LinkedQueue (const std::initializer_list<T>& il);
template <class Iterable>
explicit LinkedQueue (const Iterable& i);
//Queries
bool empty () const;
int size () const;
T& peek () const;
std::string str () const; //supplies useful debugging information; contrast to operator <<
//Commands
int enqueue (const T& element);
T dequeue ();
void clear ();
template <class Iterable>
int enqueue_all (const Iterable& i);
//Operators
LinkedQueue<T>& operator = (const LinkedQueue<T>& rhs);
bool operator == (const LinkedQueue<T>& rhs) const;
bool operator != (const LinkedQueue<T>& rhs) const;
template<class T2>
friend std::ostream& operator << (std::ostream& outs, const LinkedQueue<T2>& q);
private:
class LN;
public:
class Iterator {
public:
~Iterator();
T erase();
std::string str () const;
LinkedQueue<T>::Iterator& operator ++ ();
LinkedQueue<T>::Iterator operator ++ (int);
bool operator == (const LinkedQueue<T>::Iterator& rhs) const;
bool operator != (const LinkedQueue<T>::Iterator& rhs) const;
T& operator * () const;
T* operator -> () const;
friend std::ostream& operator << (std::ostream& outs, const LinkedQueue<T>::Iterator& i) {
outs << i.str();
return outs;
}
friend Iterator LinkedQueue<T>::begin () const;
friend Iterator LinkedQueue<T>::end () const;
private:
LN* prev = nullptr;
LN* current;
LinkedQueue<T>* ref_queue;
int expected_mod_count;
bool can_erase = true;
Iterator(LinkedQueue<T>* iterate_over, LN* initial);
};
Iterator begin () const;
Iterator end () const;
private:
class LN {
public:
LN () {}
LN (const LN& ln) : value(ln.value), next(ln.next){}
LN (T v, LN* n = nullptr) : value(v), next(n){}
T value;
LN* next = nullptr;
};
LN* front = nullptr;
LN* rear = nullptr;
int used = 0; //Cache for number of values in linked list
int mod_count = 0; //For sensing any concurrent modifications
//Helper methods
void delete_list(LN*& front);
};
Implementation (I've included only a section of my Iterator code):
template<class T>
auto LinkedQueue<T>::begin () const -> LinkedQueue<T>::Iterator {
return Iterator(this, this->front);
}
template<class T>
auto LinkedQueue<T>::end () const -> LinkedQueue<T>::Iterator {
// return Iterator(this, this->rear);
}
template<class T>
LinkedQueue<T>::Iterator::Iterator(LinkedQueue<T>* iterate_over, LN* initial) {
ref_queue = iterate_over;
expected_mod_count = iterate_over->mod_count;
current = initial;
}
The error is because Iterator begin () const; is marked as const, hence the this used in the function is const as well. So in turn, the compiler gives the error that it cannot convert from a const to non-const.
Given that the function signatures are fixed in this case, to resolve the error, added const will help solve the issue.
You could add const to the constructor signature
Iterator(LinkedQueue<T> const* iterate_over, LN* initial);
And make the member const as well
LinkedQueue<T> const* ref_queue;
Adding the const where required to ensure that members and functions remain const as required is known as being const correct.
Related
What is the right way to implement an iterator that iterates over a Recordset provided below in C++ style?
class Recordset
{
public:
Recordset(const Recordset&) = delete;
Recordset& operator = (const Recordset&) = delete;
Recordset(Recordset&& other) noexcept = default;
Recordset& operator = (Recordset&&) = default;
//Moves to the next record. Returns false if the end is reached.
bool Next();
//Gets the current record as an instance of type T.
template <class T>
void Get(T& val);
};
my idea is that I probably do something like this:
template <class T>
class Iterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
Iterator() = default;
Iterator(Recordset s) : m_i(std::move(s))
{
try_next();
}
Iterator(const Iterator&) = delete;
Iterator& operator = (const Iterator&) = delete;
Iterator(Iterator&& other) = default;
Iterator& operator = (Iterator&& other) = default;
T* operator-> () { return cur(); }
T* operator* () { return cur(); }
bool operator== (const Iterator& other) const noexcept
{
//They both are end().
return !m_v && !other.m_v;
}
bool operator!= (const Iterator& other) const noexcept
{
return !operator==(other);
}
Iterator& operator++ ()
{
this->try_next();
return *this;
}
Iterator operator++ (int)
{
Iterator tmp = *this; //would not compile.
this->try_next();
return tmp;
}
private:
bool try_next()
{
if (m_i.Next())
{
T val;
m_i.Get(val);
m_v = val;
return true;
}
return false;
}
T* cur()
{
T& val = *m_v;
return &val;
}
Recordset m_i;
std::optional<T> m_v;
};
template <class T>
std::ranges::subrange<Iterator<T>> make_range(Recordset& s)
{
return std::ranges::subrange(Iterator<T>(s), Iterator<T>{});
}
and use it as follows:
struct Record { int x; std::string y; };
int main()
{
Recordset s;
for (Record& r : make_range(s))
{
std::cout << r.x << r.y << std::endl;
}
return 0;
}
The frist question is how do I implement Iterator operator++ (int) if both Recordset and Iterator are move-only? (temp and this can't point to different records, because there is only one current record in the recordset). Does C++20 require it?
The second question is it a good idea to implement end() in this way? (end() is a simply an iterator containing an empty optional)
Single pass move-only input iterators (A c++20 std::input_iterator) are only required to be weakly incremental, where (void) ++i has the same effect as (void) i++. You can simply have void operator++(int) { ++*this; }. Older requirements for iterators (Cpp17InputIterator) requires iterators to be copyable, and require operator++ to return that copy.
And for your second question, you might want to use a sentinel type, something like:
template<typename T>
bool operator==(const Iterator<T>& it, std::default_sentinel_t) {
return !it.m_v;
}
// != can be rewritten from ==, so no need to write one
template <class T>
auto make_range(Recordset& s)
{
return std::ranges::subrange(Iterator<T>(s), std::default_sentinel);
}
And if you need to work with a algorithm that can't use separate sentinel types, use ranges::common_view. Your current solution also works, except you need to have this == &other || (!m_v && !other.m_v);.
I have the following class declaration:
#ifndef ANIL_CURSOR_LIST_H
#define ANIL_CURSOR_LIST_H
#include <cstddef>
#include <iostream>
namespace anil {
class cursor_list_node {
private:
int data;
cursor_list_node* next;
cursor_list_node* previous;
friend class cursor_list;
};
class cursor_list {
private:
// Data:
int m_index;
int m_size;
cursor_list_node* front;
cursor_list_node* back;
cursor_list_node* cursor;
// Functions:
void delete_list();
public:
cursor_list() : m_index(-1), m_size(0), front(nullptr), back(nullptr),
cursor(nullptr) {}
cursor_list(cursor_list& copied_list);
bool is_empty();
int size();
int index();
int front_data();
int back_data();
int cursor_data();
bool operator==(cursor_list& rhs); // rhs = right hand side
cursor_list& operator= (cursor_list& rhs); // rhs = right hand side
friend std::ostream& operator<<(std::ostream& out, cursor_list& rhs); // rhs = right hand side
void clear();
void move_cursor_front();
void move_cursor_back();
void move_cursor_prev();
void move_cursor_next();
void prepend(int new_data);
void append(int new_data);
void insert_before_cursor(int new_data);
void insert_after_cursor(int new_data);
void delete_front();
void delete_back();
void delete_cursor();
~cursor_list();
};
}
#endif /* ANIL_CURSOR_LIST_H */
And inside the .cpp file I have the following code for the <<operator:
std::ostream& operator<<(std::ostream& out, anil::cursor_list& rhs) {
if (rhs.is_empty() != false) {
anil::cursor_list_node* back_up_cursor = rhs.cursor;
int back_up_index = rhs.index();
for (rhs.move_cursor_front(); rhs.index() >= 0; rhs.move_cursor_next()) {
if (rhs.cursor == rhs.front) {
out << rhs.cursor_data();
} else {
out << ' ' << rhs.cursor_data();
}
}
rhs.m_index = back_up_index;
rhs.cursor = back_up_cursor;
}
}
Although I declared <<operator as a friend of the class cursor_list, I am unable to access the private member using the class_instance.private_member method. Can someone point out what I am doing wrong?
You need to define operator<< in the anil namespace.
namespace anil {
std::ostream& operator<<(std::ostream& out, cursor_list& rhs) {
// ...
return out; // don't forget this
}
}
An easier option is often to just define the friend function inline:
class cursor_list {
// ...
friend std::ostream& operator<<(std::ostream& out, cursor_list& rhs) {
// ...
return out;
}
};
It's also quite unusual to have non-const right hand side arguments to operator<< and I noticed that empty() and index() etc. are also non-const. Perhaps you have reasons for that, but it's worth a note.
How do i fix this?
I'm getting error: 'this' argument has type const but function is not marked const c++ overload operator
template <class T>
class Rational {
private:
T n = 0;
T d = 1;
public:
Rational() = default;
T numerator() {
return n;
}
T denominator() {
return d;
}
};
template <class T>
inline bool const operator ==(const Rational <T> & lhs, const Rational <T>& rhs) {
return lhs.numerator() * rhs.denominator() == lhs.denominator() * rhs.numerator();
}
My guess is that numerator() and denominator() member functions are not const member functions. Make them const. After that, the above function should work.
BTW, there is no need for the return type to be bool const. Keep it simple and change it to bool.
If numerator() and denominator() are to be used to directly assign to Rationals internal member variables as well as being used in const contexts, you need two sets of overloads. One mutable and one const:
// mutable interface
T& Rational::numerator();
T& Rational::denominator();
// const interface if T may only be a fundamental integral type
T Rational::numerator() const;
T Rational::denominator() const;
// const interface if sizeof(T) may be > sizeof(T*)
T const& Rational::numerator() const;
T const& Rational::denominator() const;
Note, only one of the const interfaces may be used so you need to select one of them.
Here's an example of how it can be done:
#include <iostream>
#include <type_traits>
template<typename T>
class Rational {
public:
// pass by value for fundamental types, by const& for other types
using by_value_or_by_const_ref =
std::conditional_t<std::is_fundamental_v<T>, T, T const&>;
Rational(by_value_or_by_const_ref n, by_value_or_by_const_ref d) :
m_numerator(n), m_denominator(d) {}
// mutable interface
T& numerator() { return m_numerator; }
T& denominator() { return m_denominator; }
// const interface
by_value_or_by_const_ref numerator() const { return m_numerator; }
by_value_or_by_const_ref denominator() const { return m_denominator; }
private:
T m_numerator;
T m_denominator;
};
template<class T>
inline bool operator==(const Rational<T>& lhs, const Rational<T>& rhs) {
// using const interface
return lhs.numerator() * rhs.denominator() ==
lhs.denominator() * rhs.numerator();
}
int main() {
Rational<int> a(10, 20);
Rational<int> b(10, 10);
// using mutable interface
a.denominator() /= 4;
b.numerator() *= 2;
std::cout << std::boolalpha << (a == b) << "\n";
}
I have a class which holds small numbers inside bigger integer variable. It works fine and fast and looks like this:
template
class Container<IntegerT>
static int bitsPerElement;
IntegerT data; // [0000] [0000] [0000] [0000] up to 128bits(or maybe more)
int size;
iterator as value_type return "reference" to container element:
Container::iterator
DataRef operator*()
DataRef
Container* parent;
int position;
But i got a problem of unavailability of std::sort, because it have no clue how to actually swap elements of this container (direct swapping of DataRefs is obviously pointless).
Is there any magic way to make std::sort work with it (actually to force it use custom swap function)?
Or is there decent alternative to std::sort which can handle this situation? (storing DataRefs in array is not considered as a solution)
Which is the fastest way to sort this data structure?
#ifndef INTSTORAGE_H
#define INTSTORAGE_H
#include <algorithm>
template<class T> class IntStorage;
template<class T>
class DataRef
{
public:
DataRef(IntStorage<T>* parent, int position) : m_parent(parent), m_position(position) {}
DataRef(DataRef&& o) = default;
DataRef(const DataRef& o) = default;
int value() const {return m_parent->value(m_position);}
void setValue(int value) {m_parent->setValue(m_position, value);}
DataRef& operator=(const DataRef& c)
{ setValue(c.value()); return *this; }
DataRef& operator=(const DataRef&& c)
{ setValue(c.value()); return *this; }
bool operator<(const DataRef& o) const
{ return value() < o.value(); }
IntStorage<T>* m_parent;
int m_position;
};
template<class T>
class IntStorage
{
template<typename> friend class IntStorage;
template<typename> friend class DataRef;
public:
void append(int value)
{
data |= (static_cast<T>(value) << (s_bitsPerItem * size));
++size;
}
void setValue(int index, T value)
{
data = ((~(s_mask << (s_bitsPerItem * index))) & data)
| (static_cast<T>(value) << (s_bitsPerItem * index));
}
T value(int i) const { return (data & s_mask << (i * s_bitsPerItem)) >> (i * s_bitsPerItem); }
class iterator
{
public:
using iterator_category = std::random_access_iterator_tag;
using difference_type = int;
using value_type = DataRef<T>;
using pointer = DataRef<T>*;
using reference = DataRef<T>&;
iterator(IntStorage<T>* parent, int pos = 0) : ref(parent, pos) {}
inline bool operator==(const iterator& o) const { return ref.m_parent == o.ref.m_parent && ref.m_position == o.ref.m_position;}
inline bool operator!=(const iterator& o) const { return !operator==(o);}
inline const DataRef<T>& operator*() const { return ref;}
inline DataRef<T>& operator*() { return ref; }
inline iterator& operator++() { ++ref.m_position; return *this; }
inline iterator& operator--() { --ref.m_position; return *this; }
inline int operator-(const iterator& o) const { return ref.m_position - o.ref.m_position; }
inline iterator operator+(int diff) const { return iterator(ref.m_parent, ref.m_position + diff); }
inline iterator operator-(int diff) const { return iterator(ref.m_parent, ref.m_position - diff); }
inline bool operator<(const iterator& o) const { return ref.m_position < o.ref.m_position; }
DataRef<T> ref;
};
friend class iterator;
iterator begin() {return iterator(this, 0);}
iterator end() {return iterator(this, size);}
iterator cbegin() {return iterator(this, 0);}
iterator cend() {return iterator(this, size);}
static constexpr T s_mask = 0b111111;
static constexpr int s_bitsPerItem = 6;
int size = 0;
T data = 0;
};
#endif // INTSTORAGE_H
I am creating my own tree-like container in C++. Here is an outline of how I imagine it's structure:
//==========================================================
// Concept
//==========================================================
//
// RootNode -> Attributes
// -> Nodes -> Attributes
// -> Nodes -> Attributes
// -> Nodes
//
// etc...
//==========================================================
I have named the type DataTree for now. The header looks like this:
#pragma once
#include <map>
#include <set>
#include <string>
namespace LB{
class DataTree;
struct DataTreeComparitor{
bool operator () (DataTree const & lhs, DataTree const & rhs) const;
bool operator () (std::string const & lhs, DataTree const & rhs) const;
bool operator () (DataTree const & lhs, std::string const & rhs) const;
};
class DataTree{
public:
template <typename Type>
Type getAttribute(std::string const & name) const{
static_cast<Type>(*m_attributes.find(name));
}
DataTree getChild(std::string const & name) const;
std::string getName(void) const;
private:
std::string m_name;
std::multimap<std::string, std::string> m_attributes;
std::multiset<DataTree, DataTreeComparitor> m_children;
};
}
I would like to find elements in the container m_children using an std::string, which DataTreeComparitor would compare against the result of getName().
However, when trying to call find() on m_children, I get the following error from mingw32:
C:/Users/Joshua/Documents/BitBucket/LBC++/LBC++/DataTree.cpp:17:31: error: no matching function for call to 'std::multiset<LB::DataTree, LB::DataTreeComparitor>::find(const string&) const'
And this using MSVC:
2 IntelliSense: no instance of overloaded function "std::multiset<_Kty, _Pr, _Alloc>::find [with _Kty=LB::DataTree, _Pr=LB::DataTreeComparitor, _Alloc=std::allocator<LB::DataTree>]" matches the argument list
argument types are: (const std::string)
object type is: const std::multiset<LB::DataTree, LB::DataTreeComparitor, std::allocator<LB::DataTree>> c:\Users\Joshua\Documents\Visual Studio 2013\Projects\Project1\Project1\Source.cpp 46 21 Project1
Here is the implementation, with the problem line indicated through a comment:
#include "DataTree.h"
namespace LB{
bool DataTreeComparitor::operator () (DataTree const & lhs, DataTree const & rhs) const{
return lhs.getName() < rhs.getName();
}
bool DataTreeComparitor::operator () (std::string const & lhs, DataTree const & rhs) const{
return lhs < rhs.getName();
}
bool DataTreeComparitor::operator () (DataTree const & lhs, std::string const & rhs) const{
return lhs.getName() < rhs;
}
DataTree DataTree::getChild(std::string const & name) const{
return *m_children.find(name); // problem line
}
}
I cannot for the life of me work out why the compiler isn't using DataTreeComparitor to compare DataTree against an std::string.
My question is, why isn't it doing so? As you can see, I've tried adding operator overloads to the comparitor where std::string is both the left hand operator and the right hand.
Note:
I know there are many essential features things missing from this DataTree type; it is still a work in progress.
The problem is that you are calling multiset<DataTree, DataTreeComparitor>::find with an std::string parameter. That cannot work, because it requires a DataTree, and there is no conversion from std::string to DataSet.
You could fix this by adding an implicit converting constructor:
class DataTree{
public:
DataTree(const std::string& name) : m_name(name) {}
....
};