Iterators and Const iterators in C++? - c++

Let's suppose I have two classes, the first:
class IntMatrix::iterator {
private:
const IntMatrix *int_matrix;
int index;
iterator(const IntMatrix *int_matrix, int index);
friend class IntMatrix;
public:
int &operator*() const;
iterator &operator++();
iterator operator++(int);
bool operator==(const iterator &it) const;
bool operator!=(const iterator &it) const;
iterator(const iterator &) = default;
iterator &operator=(const iterator &) = default;
~iterator() = default;
};
and the second is:
class IntMatrix::const_iterator {
private:
const IntMatrix *int_matrix;
int index;
const_iterator(const IntMatrix *int_matrix, int index);
friend class IntMatrix;
public:
const int &operator*() const;
const_iterator &operator++();
const_iterator operator++(int);
bool operator==(const const_iterator &it) const;
bool operator!=(const const_iterator &it) const;
const_iterator(const const_iterator &) = default;
const_iterator &operator=(const const_iterator &) = default;
~const_iterator() = default;
};
How may I prevent code duplication here, since the implementation is 99% the same?
How about generics may it help here or inheritance?
An example of how they are implemented:
int &IntMatrix::iterator::operator*() const {
return int_matrix->data[index];
}
const int &IntMatrix::const_iterator::operator*() const {
return int_matrix->data[index];
}
Plus, I want In main to allow something like:
IntMatrix::iterator it;
Update:
I'm trying to implement the given solution on a Generic class called Matrix in the following way: (Note the code shown is all written as public in my class)
template<typename T>
class iterator_impl;
template<typename T>
iterator_impl<T> begin(){
return iterator(this, 0);
}
template<typename T>
iterator_impl<T> end(){
return iterator(this, size());
}
template<typename T>
iterator_impl<const T> begin() const
{
return const_iterator(this, 0);
}
template<typename T>
iterator_impl<const T> end() const
{
return const_iterator(this, size());
}
template<typename T>
class iterator_impl{
private:
const Matrix<T> *matrix;
int index;
friend class Matrix<T>;
public:
iterator_impl(const iterator_impl &) = default;
iterator_impl &operator=(const iterator_impl &) = default;
~iterator_impl() = default;
iterator_impl(const Matrix<T> *int_matrix, int index)
: matrix(int_matrix), index(index) {}
iterator_impl &operator++()
{
++index;
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result = *this;
++*this;
return result;
}
bool operator==(const iterator_impl &it) const
{
return index == it.index;
}
bool operator!=(const iterator_impl &it) const
{
return !(*this == it);
}
T &operator*() const {
if (index < 0 || index > matrix->size() - 1) {
throw Matrix<T>::AccessIllegalElement();
}
return matrix->data[index];
}
};
template<typename T>
using iterator = iterator_impl<T>;
template<typename T>
using const_iterator = iterator_impl<const T>;
and I'm getting some errors like:
invalid use of 'this' outside of a non-static member function
return iterator(this, 0);

One way is to make the implementation into a class template and then to make aliases for the const and non-const instantiations.
Plus, I want In main to allow something like:
IntMatrix::iterator it;
You then need to add a default constructor.
Example:
#include <cstddef>
#include <type_traits>
// in the .hpp file:
class IntMatrix {
private:
int data[10]; // just an example
size_t size = 10; // just an example
template<typename T>
class iterator_impl;
public:
// two typedefs using the template:
using iterator = iterator_impl<int>;
using const_iterator = iterator_impl<const int>;
const_iterator cbegin() const;
const_iterator cend() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
};
// still in the .hpp file:
template<typename T>
class IntMatrix::iterator_impl {
public:
using matrix_type =
std::conditional_t<
std::is_const_v<T>,
const IntMatrix,
IntMatrix
>;
private:
matrix_type* int_matrix;
size_t index;
friend IntMatrix;
iterator_impl(matrix_type* im, size_t idx) :
int_matrix(im), index(idx)
{}
public:
iterator_impl() = default; // default constructor
//iterator_impl(const iterator_impl&) = default; // not needed
//iterator_impl &operator=(const iterator_impl &) = default; // not needed
//~iterator_impl() = default; // not needed
iterator_impl &operator++() {
++index;
return *this;
}
iterator_impl operator++(int) {
iterator_impl old(*this);
++index;
return old;
}
bool operator!=(const iterator_impl &it) const {
return index != it.index || int_matrix != it.int_matrix;
}
bool operator==(const iterator_impl &it) const {
return !(*this != it);
}
T& operator*() const {
return int_matrix->data[index];
}
};
// in the .cpp file:
IntMatrix::const_iterator IntMatrix::cbegin() const { return {this, 0}; }
IntMatrix::const_iterator IntMatrix::cend() const { return {this, size}; }
IntMatrix::const_iterator IntMatrix::begin() const { return cbegin(); }
IntMatrix::const_iterator IntMatrix::end() const { return cend(); }
IntMatrix::iterator IntMatrix::begin() { return {this, 0}; }
IntMatrix::iterator IntMatrix::end() { return {this, size}; }
Demo
Edit: If Matrix is a class template itself, you need to change the iterator slightly.
#include <cstddef>
#include <type_traits>
// in the .hpp file:
template<typename T>
class Matrix {
private:
T data[10]; // just an example
size_t size = 10; // just an example
template<typename I>
class iterator_impl;
public:
// two typedefs using the template:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
auto cbegin() const;
auto cend() const;
auto begin() const;
auto end() const;
auto begin();
auto end();
};
// still in the .hpp file:
template<typename T>
template<typename I>
class Matrix<T>::iterator_impl {
public:
using value_type = std::remove_const_t<I>;
using matrix_type =
std::conditional_t<
std::is_const_v<I>,
const Matrix<value_type>,
Matrix<value_type>
>;
private:
matrix_type* matrix;
size_t index;
friend Matrix;
iterator_impl(matrix_type* im, size_t idx) :
matrix(im), index(idx)
{}
public:
iterator_impl() = default; // default constructor
iterator_impl& operator++() {
++index;
return *this;
}
iterator_impl operator++(int) {
iterator_impl old(*this);
++index;
return old;
}
bool operator!=(const iterator_impl &it) const {
return index != it.index || matrix != it.matrix;
}
bool operator==(const iterator_impl &it) const {
return !(*this != it);
}
I& operator*() const {
return matrix->data[index];
}
};
// still in the .hpp file
template<typename T> auto Matrix<T>::cbegin() const { return const_iterator{this, 0}; }
template<typename T> auto Matrix<T>::cend() const { return const_iterator{this, size}; }
template<typename T> auto Matrix<T>::begin() const { return cbegin(); }
template<typename T> auto Matrix<T>::end() const { return cend(); }
template<typename T> auto Matrix<T>::begin() { return iterator{this, 0}; }
template<typename T> auto Matrix<T>::end() { return iterator{this, size}; }
Demo

Related

invalid use of 'this' outside of a non-static member function?

I wrote the following code inside the public part in my Matrix<T> class:
#include <iostream>
#include "Auxiliaries.h"
namespace mtm {
template<class T>
class Matrix {
private:
Dimensions dimensions;
T *data;
public:
iterator_impl<T> begin(){};
class AccessIllegalElement;
class IllegalInitialization;
class DimensionMismatch;
Matrix(const Dimensions &matrix_dimensions, const T &initial_value = T());
};
/**Iterators**/
template<typename T>
class iterator_impl;
template<typename T>
iterator_impl<T> begin(){
return iterator(this, 0);
}
template<typename T>
class iterator_impl{
private:
const Matrix<T> *matrix;
int index;
friend class Matrix<T>;
public:
iterator_impl(const iterator_impl &) = default;
iterator_impl &operator=(const iterator_impl &) = default;
~iterator_impl() = default;
iterator_impl(const Matrix<T> *matrix, int index)
: matrix(matrix), index(index) {}
iterator_impl &operator++()
{
++index;
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result = *this;
++*this;
return result;
}
bool operator==(const iterator_impl &it) const
{
return index == it.index;
}
bool operator!=(const iterator_impl &it) const
{
return !(*this == it);
}
T &operator*() const {
return matrix->data[index];
}
};
template<typename T>
using iterator = iterator_impl<T>;
template<typename T>
using const_iterator = iterator_impl<const T>;
}
But I'm getting the following error:
invalid use of 'this' outside of a non-static member function
return iterator(this, 0);
What did I do wrong here and how may I solve this one?
my class:
template<class T>
class Matrix {
private:
Dimensions dimensions;
T *data;
public:
iterator_impl<T> begin(){};
//....
}
https://wandbox.org/permlink/R4rQjGVNZWUHtMqj
What should this mean in this context?
template<typename T>
iterator_impl<T> begin(){
return iterator(this, 0);
}
It should be a pointer to the object of the class the method belongs to, but the begin function is a free function that is not a member of any class.
How do you plan to use this function? Basic patterns are:
container.begin();
or
begin(container);
In both cases there is a container: the object of the class you have forgotten in your sample.
Update
According you to your updates, this should be the pointer to the Matrix object. You have already declared the method, not let's define it. The implementation is actually up to you, I'm not sure if it is correct. The key part is that you have forgotten to specify the class in the function signature:
template<typename T>
iterator_impl<T> Matrix<T>::begin() {
return iterator(this, 0);
}

Interface which declares functions for iteration

I have an assignment in which I need to make template classes LinkedList and Traversible. Class Traversible needs to be a interface which declares functions for indexing and iteration of some collection class. I don't exactly know how to make an interface for iterator so LinkedList can use it. I was thinking something like
template <class T, class U>
class ITraversible {
public:
virtual U begin() noexcept = 0;
virtual U end() noexcept = 0;
virtual T& operator[](int) = 0;
};
and then in LinkedList header file I would do:
template <class T>
class LinkedList : public ITraversible<T,typename LinkedList<T>::iterator> {
struct node {
T data;
node* next, *prev;
explicit node(const T&);
void connect(node*);
};
node *head, *tail;
int n;
public:
/*************************ITERATOR************************/
class iterator : public std::iterator<std::bidirectional_iterator_tag, node*> {
typename LinkedList<T>::node* itr;
explicit iterator(node*) noexcept;
friend class LinkedList;
public:
iterator& operator++();
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
bool operator==(const iterator&) const noexcept;
bool operator!=(const iterator&) const noexcept;
T& operator*() const noexcept;
T& operator->() const noexcept;
};
/**********************************************************/
LinkedList() noexcept;
LinkedList(std::initializer_list<T>);
LinkedList(const LinkedList&);
LinkedList(LinkedList&&) noexcept;
~LinkedList() noexcept;
LinkedList& operator=(LinkedList) noexcept;
template <class A>
friend void swap(LinkedList<A>&, LinkedList<A>&);
void add(const T&);
void removeAt(int);
int size() const noexcept;
bool operator==(const LinkedList&) const noexcept;
bool operator!=(const LinkedList&) const noexcept;
virtual T& operator[](int) override;
virtual iterator begin() noexcept override;
virtual iterator end() noexcept override;
};
But then Traversable template has two parameters and it should have only one.
Is this what I am supposed to do? Keep in mind I am new to templates and iterators.
When creating an interface you'll need to nail down the static types of what is being returned. These may behave dynamically different but you can't change the type other than using a subtype relation when returning pointers or references.
Personally, I think this exercise is ill-advised for a C++ context. It may make some sense when using Java or C#. However, similar behavior can be obtained. A rought sketch would be something like this (although this should work it will be rather slow):
template <typename T>
struct iterator_base {
virtual iterator_base() {}
virtual iterator_base<T>* do_clone() = 0;
virtual T& do_value() = 0;
virtual void do_next() = 0;
virtual bool do_equal() = 0;
// other operations to implement operator--, operator[], ...
};
template <typename It>
class iterator: iterator_base<typename std::iterator_traits<It>::value_type> {
typedef typename std::iterator_traits<It>::value_type> type;
It it;
iterator_base<type>* do_clone() { return new iterator<It>(*this); }
type& do_value() { return *this->it; }
void do_next() { ++this->it; }
bool do_equal(iterator_base<type>* other) {
return this->it == static_cast<iterator<It>>(other)->it;
}
};
template <typename T>
class poly_iterator {
std::unique_ptr<iterator_base<T>> ptr;
public:
poly_iterator(iterator_base<T>* ptr): ptr(ptr) {}
poly_iterator(poly_iterator const& other): ptr(other.ptr->clone()) {}
poly_iterator& operator= (poly_iterator other) {
other.swap(this);
return *this;
}
void swap(poly_iterator& other) { swap(this->ptr, other.ptr); }
T& operator*() { return this->ptr->value(); }
T* operator->() { return &this->operator*(); }
poly_iterator& operator++() { this->ptr->next(); return *this; }
poly_iterator operator++(int) {
poly_iterator rc(*this);
this->operator++();
return rc;
}
bool operator== (poly_iterator const& other) {
return this->ptr->equal(other.ptr.ptr());
}
bool operator!= (poly_iterator const& other) {
return !(*this == other);
}
// other operations
};
// define a suitable specialization of std::iterator_traits<poly_iterator<T>>
template <typename T>
class ITraversible {
virtual iterator_base<T>* do_begin() = 0;
virutal iterator_base<T>* do_end() = 0;
public:
poly_iterator<T> begin() { return this->do_begin(); }
poly_iterator<T> end() { return this->do_end(); }
// other operations
};
template <typename T>
class List: public ITraversible<T> {
std::list<T> list;
iterator_base<T>* do_begin() {
return iterator<std::list<T>::iterator>(list.begin());
}
iterator_base<T>* do_end() {
return iterator<std::list<T>::iterator>(list.end());
}
public:
// whatever is needed to fill the list
};

Unexpected End of File error on .hpp File

I'm working on a file, Deque.hpp, which uses an iterator class and Nodes to make a deque, but I'm getting the error:
"Error C1004 unexpected end-of-file found "
I can't seem to find what's causing this. Here's my code.
#ifndef DEQUE_H
#define DEQUE_H
#include <iostream>
#include <new>
#include <cstdlib>
#include <algorithm>
//
//
//
template <typename T>
class Deque
{
public:
Deque();
Deque(const Deque & rhs);
~Deque();
Deque & operator= (const Deque & rhs);
Deque(Deque && rhs) : theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail };
Deque & operator= (Deque && rhs);
T & Deque<T>::operator[](int n);
Iterator begin();
const_iterator begin() const;
Iterator end();
const_iterator end() const;
int size() const;
bool isEmpty() const;
void clear();
T & left();
const T & left() const;
T & right();
const T & right() const;
void pushLeft(const T & x);
void pushLeft(T && x);
void pushRight(const T & x);
void pushRight(T && x);
T & popLeft();
T & popRight();
bool Deque<T>::contains(const T&);
struct Node
{
T data;
Node *prev;
Node *next;
Node(const T & d = T{}, Node * p = NULL, Node * n = NULL) :
data{ d }, prev{ p }, next{ n } {}
Node(T && d, Node * p = NULL, Node * n = NULL)
: data{ std::move(d) }, prev{ p }, next{ n } {}
};
class const_iterator
{
public:
const_iterator() : current{ NULL } { }
const T & operator* () const;
const_iterator & operator++ ();
const_iterator & operator-- (); //!
const_iterator operator++ (int);
const_iterator operator-- (int); //!
bool operator== (const const_iterator & rhs) const;
bool operator!= (const const_iterator & rhs) const;
protected:
Node *current;
T & retrieve() const;
const_iterator(Node *p) : current{ p } { }
friend class Deque<T>;
};
class Iterator : public const_iterator
{
public:
Iterator();
T & operator* ();
const T & operator* () const;
Iterator & operator++ ();
Iterator & operator-- (); //!
Iterator operator++ (int);
Iterator operator-- (int); //!
protected:
Iterator(Node *p) : const_iterator{ p } { }
friend class Deque<T>;
};
private:
// Insert x before itr.
Iterator insert(Iterator itr, const T & x);
// Insert x before itr.
Iterator insert(Iterator itr, T && x);
// Erase item at itr.
Iterator erase(Iterator itr);
Iterator erase(Iterator from, Iterator to);
int theSize;
Node *head;
Node *tail;
void init();
};
template<typename T>
inline const T & Deque<T>::const_iterator::operator*() const
{
return retrieve();
}
template<typename T>
inline const_iterator & Deque<T>::const_iterator::operator++()
{
current = current->next;
return *this;
}
template<typename T>
inline const_iterator & Deque<T>::const_iterator::operator--()
{
current = current->prev;
return *this;
}
template<typename T>
inline const_iterator Deque<T>::const_iterator::operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
template<typename T>
inline const_iterator Deque<T>::const_iterator::operator--(int)
{
const_iterator old = *this;
--(*this);
return old;
}
template<typename T>
inline bool Deque<T>::const_iterator::operator==(const const_iterator & rhs) const
{
return current == rhs.current;
}
template<typename T>
inline bool Deque<T>::const_iterator::operator!=(const const_iterator & rhs) const
{
return !(*this == rhs);
}
template<typename T>
inline T & Deque<T>::const_iterator::retrieve() const
{
return current->data;
}
template<typename T>
inline Deque<T>::Iterator::Iterator()
{
}
template<typename T>
inline T & Deque<T>::Iterator::operator*()
{
return const_iterator::retrieve();
}
template<typename T>
inline const T & Deque<T>::Iterator::operator*() const
{
return const_iterator::operator*();
}
template<typename T>
inline Iterator & Deque<T>::Iterator::operator++()
{
this->current = this->current->next;
return *this;
}
template<typename T>
inline Iterator & Deque<T>::Iterator::operator--()
{
this->current = this->current->prev;
return *this;
}
template<typename T>
inline Iterator Deque<T>::Iterator::operator++(int)
{
Iterator old = *this;
++(*this);
return old;
}
template<typename T>
inline Iterator Deque<T>::Iterator::operator--(int)
{
Iterator old = *this;
--(*this);
return old;
}
template<typename T>
inline Deque<T>::Deque()
{
init();
}
template<typename T>
inline Deque<T>::~Deque()
{
clear();
delete head;
delete tail;
}
template<typename T>
inline Deque & Deque<T>::operator=(const Deque & rhs)
{
if (this == &rhs)
return *this;
clear();
for (const_iterator itr = rhs.begin(); itr != rhs.end(); ++itr)
pushRight(*itr)
return *this;
}
template<typename T>
inline Deque<T>::Deque(Deque && rhs)
{
rhs.theSize = 0;
rhs.head = NULL;
rhs.tail = NULL;
}
template<typename T>
inline Deque & Deque<T>::operator=(Deque && rhs)
{
std::swap(theSize, rhs.theSize);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
template<typename T>
inline T & Deque<T>::operator[](int n)
{
Iterator itr = begin();
for (int i = 0; i < n; i++)
{
itr.current = itr.current->next;
}
return itr.current->data;
}
template<typename T>
inline Iterator Deque<T>::begin()
{
return{ head->next };
}
template<typename T>
inline const_iterator Deque<T>::begin() const
{
return{ head->next };
}
template<typename T>
inline Iterator Deque<T>::end()
{
return{ tail->prev }; //changed to -> prev
}
template<typename T>
inline const_iterator Deque<T>::end() const
{
return{ tail->prev };
}
template<typename T>
inline int Deque<T>::size() const
{
return theSize;
}
template<typename T>
inline bool Deque<T>::isEmpty() const
{
return size() == 0;
}
template<typename T>
inline void Deque<T>::clear()
{
while (!isEmpty())
popLeft();
}
template<typename T>
inline T & Deque<T>::left()
{
return *begin();
}
template<typename T>
inline const T & Deque<T>::left() const
{
return *begin();
}
template<typename T>
inline T & Deque<T>::right()
{
return *end(); // deleted "--*end"
}
template<typename T>
inline const T & Deque<T>::right() const
{
return *end();
}
template<typename T>
inline void Deque<T>::pushLeft(const T & x)
{
insert(begin(), x);
}
template<typename T>
inline void Deque<T>::pushLeft(T && x)
{
insert(begin(), std::move(x)); // changed std::move(x)) to x
}
template<typename T>
inline void Deque<T>::pushRight(const T & x)
{
insert(end(), x);
}
template<typename T>
inline void Deque<T>::pushRight(T && x)
{
insert(end(), std::move(x));
}
template<typename T>
inline T & Deque<T>::popLeft()
{
return *begin(); erase(begin());
}
template<typename T>
inline T & Deque<T>::popRight()
{
return *end(); erase(end()); // changed --end to end
}
template<typename T>
inline bool Deque<T>::contains(const T &)
{
// stuff here
}
template<typename T>
inline Iterator Deque<T>::insert(Iterator itr, const T & x)
{
Node *p = itr.current;
theSize++;
return{ p->prev = p->prev->next = new Node{ x, p->prev, p } };
}
template<typename T>
inline Iterator Deque<T>::insert(Iterator itr, T && x)
{
Node *p = itr.current;
theSize++;
return{ p->prev = p->prev->next = new Node{ std::move(x), p->prev, p } };
}
template<typename T>
inline Iterator Deque<T>::erase(Iterator itr)
{
Node *p = itr.current;
Iterator retVal{ p->next };
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return retVal;
}
template<typename T>
inline Iterator Deque<T>::erase(Iterator from, Iterator to)
{
for (Iterator itr = from; itr != to; )
itr = erase(itr);
return to;
}
template<typename T>
inline void Deque<T>::init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
template<typename T>
inline Deque<T>::Deque(const Deque & rhs)
{
init();
*this = rhs;
}
#endif
The first error to fix is the one identified by Algirdas Preidžius: your constructor has an initialization list but no body.
You should replace
Deque(Deque && rhs) : theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail };
by
Deque(Deque && rhs) : theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail } {};
Then you'll have a lot of other errors to fix. Good luck !
My advice when coding: write your code incrementaly, build and test regularly. You shouldn't have so much errors to fix at once !
By the way, any reason to not use std::deque ?

Implementing a list iterator and operators: -- ++ *

Following on from this question: std::list implementation & pointer arithemetic.
I want to implement a list iterator that is interchangeable with other common containers types and their respective iterators, so I want to use operators such as: --, ++, * and be able to declare iterators as normal, so: list::iterator iter = list.begin();
The --, ++ operators now work as they should, but I ran up against the problem of de-referencing the iterator, as structs can't return a value: T iterator::operator*()
template <class T>
struct element {
element<T> *prev = NULL;
element<T> *next = NULL;
T data;
};
template <typename T>
class list {
public:
list::list();
element<T>* current;
struct iterator{
element<T>* iterator::operator++(){
this = *this->next; ..whatever it works
}
element<T>* iterator::operator--()
T iterator::operator*()
}
};
the iterators could look something like this:
(declared inside the list template)
struct iterator;
struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T>
{
const_iterator() = default;
T operator*() { return itm->data; }
const T* operator->() { return &(itm->data); }
const_iterator operator++() { itm = itm->next; return *this; }
const_iterator operator--() { itm = itm->prev; return *this; }
const_iterator operator++(int) { const_iterator ret=*this; itm = itm->next; return ret; }
const_iterator operator--(int) { const_iterator ret=*this; itm = itm->prev; return ret; }
bool operator==(const_iterator oth) const { return itm==oth.itm; }
bool operator!=(const_iterator oth) const { return itm!=oth.itm; }
private:
element<T>* itm = nullptr;
const_iterator(element<T>* i) : itm(i) {}
friend
class list;
friend
struct iterator;
};
struct iterator : public std::iterator<std::bidirectional_iterator_tag, T>
{
iterator() = default;
T& operator*() { return itm->data; }
T* operator->() { return &(itm->data); }
iterator operator++() { itm = itm->next; return *this; }
iterator operator--() { itm = itm->prev; return *this; }
iterator operator++(int) { iterator ret=*this; itm = itm->next; return ret; }
iterator operator--(int) { iterator ret=*this; itm = itm->prev; return ret; }
bool operator==(iterator oth) const { return itm==oth.itm; }
bool operator!=(iterator oth) const { return itm!=oth.itm; }
operator const_iterator() { return {itm}; }
private:
element<T>* itm = nullptr;
iterator(element<T>* i) : itm(i) {}
friend
class list;
};
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
this needs
#include <iterator>
to compile

C++11 compliant set with custom keys (key_type not value_type but functor getting the key of value)

I am searching for template library with set-like container allowing searching by different key. I don't want map (key duplication) and want C++11 compliant code (C++14 added template<class K> iterator std::set::find(const K& x) which could be used in std::set<T*,my_transparent_deref_less<T*> > with custom compare functor).
Do you know such? Will boost add such or does it have already?
The signature should look like this: the_set<T, GetKey, Compare> and I want structure optimized for both size / memory usage (thus flat_set / btree_set) and speed of searching (insert/remove speed is not that critical). Example:
class User {
public:
User(const char *name);
const char *name();
... };
the_set<User*,const char*> USERS([](User* u) { u->name(); },
[](const char* lhs, const char* rhs) { strcmp(lhs, rhs) < 0; });
I have found red-black-tree in boost::detail that looks like what I want - the signature is template <class Key, class Value, class KeyOfValue, class KeyCompare, class A> class rbtree. Do we have something like that with flat_set and btree_set that I could use (without the fear of using something that is not to be used publicly but purposedly hidden as detail)?
Reason: I do plan to use such sets for many objects and many keys (possibly different keys/sets for same objects).
USERS, UNITS, ... - global using btree_set, possibly something like boost::multi_index
User::units, ... - sets in objects using flat_set
My code so far: (The problem is that I have to use StringWrapper now)
#include <set>
#include <iostream>
#include <type_traits>
#include "btree_set.h"
#include "boost/container/flat_set.hpp"
// dereferencing comparator
template <class T>
class less: public std::less<T> {
public:
typename std::enable_if<std::is_pointer<T>::value,
bool>::type operator() (T lhs, T rhs) const {
return *lhs < *rhs; }};
// here I can change underlying structure to btree_set or std::set
template <class T,
class C = less<T>,
class A = std::allocator<T> >
using default_set = boost::container::flat_set<T, C, A>;
// this works fine for classes derived from their primary key
template <class T, class K = T,
class B = default_set<K*> >
class object_set {
private:
typename std::enable_if<std::is_base_of<K, T>::value,
B>::type impl;
public:
template<class... Args>
T* add(K* key, Args&& ...args) {
auto it = impl.insert(key);
if (!it.second) return nullptr;
T* value = new T(*key, std::forward<Args>(args)...);
*it.first = value;
return value; }
T* operator[](K* key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
return (T*)*it; }
T* remove(K* key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
T* value = (T*)*it;
impl.erase(it);
return value; }
public:
template<class... Args>
T* add(K key, Args&& ...args) {
return add(&key, std::forward<Args>(args)...); }
T* operator[](K key) {
return (*this)[&key]; }
T* remove(K key) {
return remove(&key); }};
// workaround for above std::is_base_of constraint
class StringWrapper {
const char *data;
public:
StringWrapper(const char *data) {
this->data = data; }
operator const char *() const {
return data; }};
// example of class I want to use the container on
class User: public StringWrapper {
public:
User(const char *name): StringWrapper(name) {}};
// testing
object_set<User,StringWrapper> USERS;
int main() {
USERS.add("firda"); USERS.add("firda2");
User* firda = USERS["firda"];
delete USERS.remove(firda);
delete USERS.remove("firda2"); }
Sounds like a job for boost::multi_index
http://www.boost.org/doc/libs/1_55_0/libs/multi_index/doc/index.html
This is what I came with:
#include "boost/container/flat_set.hpp"
template<class T, class K, class GetKey, class CmpKey>
class fset {
private:
boost::container::container_detail::flat_tree<
K, T*, GetKey, CmpKey, std::allocator<T*> >
impl;
public:
template<class... Args>
T* add(K key, Args&& ...args) {
auto it = impl.lower_bound(key);
if (it != impl.end() && impl.key_comp()(key, GetKey()(*it))) {
return nullptr; }
T* value = new T(key, std::forward<Args>(args)...);
impl.insert_unique(it, value);
return value; }
T* operator[](K key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
return *it; }
T* remove(K key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
T* value = *it;
impl.erase(it);
return value; }};
class User {
private:
const char *name_;
public:
User(const char *name) {
std::size_t size = std::strlen(name) + 1;
char *buf = new char[size];
std::memcpy(buf, name, size);
name_ = buf; }
~User() {
delete[] name_; }
const char *name() const {
return name_; }
public:
struct get_name {
const char *operator()(User* u) const {
return u->name(); }};
struct cmp_name {
bool operator()(const char* lhs, const char* rhs) const {
return std::strcmp(lhs, rhs) < 0; }};};
fset<User,const char*,User::get_name,User::cmp_name>
USERS;
int main() {
USERS.add("firda");
User* firda = USERS["firda"];
delete USERS.remove("firda"); }
Should I close or delete this question now?
This is what I use now (look at struct vset_adaptor)
#ifndef HEADER___VECTSET___BE8EB41D7B3971E1
#define HEADER___VECTSET___BE8EB41D7B3971E1
#include <vector>
//############################################################### ptrvect
template <class T, class base = std::vector<T*> >
class ptrvect: public base {
public:
class iterator: public base::iterator {
friend class ptrvect;
private:
iterator(const typename base::const_iterator& it):
base::iterator(const_cast<T**>(&*it)) {
return; }
public:
iterator(const typename base::iterator& it):
base::iterator(it) {
return; }
T* operator->() const {
return **this; }};
class const_iterator: public base::const_iterator {
public:
const_iterator(const typename base::const_iterator& it):
base::const_iterator(it) {
return; }
const_iterator(const typename base::iterator& it):
base::const_iterator(it) {
return; }
T* operator->() const {
return **this; }};
template <class It = iterator>
class condpair: public std::pair<It,bool> {
public:
condpair(It it, bool second):
std::pair<It,bool>(it, second) {
return; }
T* operator->() const {
return *std::pair<It,bool>::first; }};
public:
iterator begin() {
return iterator(base::begin()); }
iterator end() {
return iterator(base::end()); }
const_iterator begin() const {
return const_iterator(base::begin()); }
const_iterator end() const {
return const_iterator(base::end()); }
public: // workarounds for pre-C++11 / bad C++11 implementation (should allow const_iterator)
iterator insert(const_iterator pos, T* value) {
return base::insert(iterator(pos), value); }
iterator erase(const_iterator pos) {
return base::erase(iterator(pos)); }
public: // addons
iterator find (T* key) {
return std::find(begin(), end(), key); }
const_iterator find (T* key) const {
return std::find(begin(), end(), key); }
bool contains (T* key) const {
return find(key) != end(); }
T* remove(T* key) {
auto it = find(key);
if (it == end()) return null;
T* val = *it;
base::erase(it);
return val; }
T* add(T* val) {
base::push_back(val);
return val; }
void release() {
for (T* it : *this) delete it;
base::clear(); }};
//########################################################## vset adaptor
template <class T, class K>
struct vset_adaptor {
K operator()(T* it) const {
return (K)(*it); }
bool operator()(K lhs, K rhs) const {
return lhs < rhs; }};
template <class T>
struct vset_adaptor<T,T*> {
T* operator()(T* it) const {
return it; }
bool operator()(T* lhs, T* rhs) const {
return lhs < rhs; }};
//================================================================== vset
template <class T, class K=T*, class F = vset_adaptor<T,K> >
class vset {
private:
ptrvect<T> impl;
struct Comp {
F f;
K operator()(T* it) const {
return f(it); }
bool operator()(K lhs, K rhs) const {
return f(lhs, rhs); }
bool operator()(T* lhs, K rhs) const {
return f(f(lhs), rhs); }
bool operator()(K lhs, T* rhs) const {
return f(lhs, f(rhs)); }
bool operator()(T* lhs, T* rhs) const {
return f(f(lhs), f(rhs)); }};
Comp comp;
public:
typedef typename ptrvect<T>::const_iterator iterator, const_iterator;
typedef unsigned size_type;
typedef T *value_type;
typedef K key_type;
typedef typename ptrvect<T>::template condpair<iterator> condpair;
public:
iterator begin() const {
return iterator(impl.begin()); }
iterator end() const {
return iterator(impl.end()); }
size_type size() const {
return impl.size(); }
bool empty() const {
return impl.empty(); }
public:
iterator lower_bound(K key) const {
return std::lower_bound(impl.begin(), impl.end(), key, comp); }
iterator upper_bound(K key) const {
return std::upper_bound(impl.begin(), impl.end(), key, comp); }
std::pair<iterator, iterator> equal_range(K key) const {
return std::equal_range(impl.begin(), impl.end(), key, comp); }
iterator find(K key) const {
iterator it = lower_bound(key);
return it == end() || comp(key, *it) ? end() : it; }
bool contains(K key) const {
iterator it = lower_bound(key);
return it != end() && !comp(key, *it); }
public:
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type lower_bound(T* key) const {
return std::lower_bound(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type upper_bound(T* key) const {
return std::upper_bound(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
std::pair<iterator, iterator> >::type equal_range(T* key) const {
return std::equal_range(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type find(T* key) const {
iterator it = lower_bound(comp(key));
return it == end() || comp(key, *it) ? end() : it; }
public:
template<class... Args>
condpair emplace(K key, Args&& ...args) {
iterator it = lower_bound(key);
if (it == end() || comp(key, *it)) {
return condpair(impl.insert(it,
new T(key, std::forward<Args>(args)...)), true); }
return condpair(it, false); }
iterator erase(iterator at) {
return impl.erase(at); }
public:
T* add(T* value) {
iterator it = lower_bound(value);
if (it == end() || comp(comp(value), *it)) {
impl.insert(it, value);
return value; }
return nullptr; }
template<class... Args>
T* add(K key, Args&& ...args) {
iterator it = lower_bound(key);
if (it == end() || comp(key, *it)) {
T* value = new T(key, std::forward<Args>(args)...);
impl.insert(it, value);
return value; }
return nullptr; }
T* get(K key) const {
iterator it = find(key);
return it == impl.end() ? nullptr : *it; }
T* operator[](K key) const {
return *emplace(key).first; }
T* remove(K key) {
iterator it = find(key);
if (it == impl.end()) return nullptr;
T* value = *it;
impl.erase(it);
return value; }
typename std::enable_if<!std::is_same<T*,K>::value,
T*>::type remove(T* key) {
return remove(comp(key)); }
void release() {
for (T* it : *this) {
delete it; }
impl.clear(); }
void clear() {
impl.clear(); }};
#endif
....if you wonder about the codestyling, it is output of my own preprocessor. This is the real code:
#include <vector>
//############################################################### ptrvect
template <class T, class base = std::vector<T*> >
class ptrvect: public base
public:
class iterator: public base::iterator
friend class ptrvect
private:
iterator(const typename base::const_iterator& it):
base::iterator(const_cast<T**>(&*it))
return
public:
iterator(const typename base::iterator& it):
base::iterator(it)
return
T* operator->() const
return **this
class const_iterator: public base::const_iterator
public:
const_iterator(const typename base::const_iterator& it):
base::const_iterator(it)
return
const_iterator(const typename base::iterator& it):
base::const_iterator(it)
return
T* operator->() const
return **this
template <class It = iterator>
class condpair: public std::pair<It,bool>
public:
condpair(It it, bool second):
std::pair<It,bool>(it, second)
return
T* operator->() const
return *std::pair<It,bool>::first
public:
iterator begin()
return iterator(base::begin())
iterator end()
return iterator(base::end())
const_iterator begin() const
return const_iterator(base::begin())
const_iterator end() const
return const_iterator(base::end())
public: // workarounds for pre-C++11 / bad C++11 implementation (should allow const_iterator)
iterator insert(const_iterator pos, T* value)
return base::insert(iterator(pos), value)
iterator erase(const_iterator pos)
return base::erase(iterator(pos))
public: // addons
iterator find (T* key)
return std::find(begin(), end(), key)
const_iterator find (T* key) const
return std::find(begin(), end(), key)
bool contains (T* key) const
return find(key) != end()
T* remove(T* key)
auto it = find(key)
if it == end(); return null
T* val = *it
base::erase(it)
return val
T* add(T* val)
base::push_back(val)
return val
void release()
for T* it : *this; delete it
base::clear()
//########################################################## vset adaptor
template <class T, class K>
struct vset_adaptor
K operator()(T* it) const
return (K)(*it)
bool operator()(K lhs, K rhs) const
return lhs < rhs
template <class T>
struct vset_adaptor<T,T*>
T* operator()(T* it) const
return it
bool operator()(T* lhs, T* rhs) const
return lhs < rhs
//================================================================== vset
template <class T, class K=T*, class F = vset_adaptor<T,K> >
class vset
private:
ptrvect<T> impl
struct Comp
F f
K operator()(T* it) const
return f(it)
bool operator()(K lhs, K rhs) const
return f(lhs, rhs)
bool operator()(T* lhs, K rhs) const
return f(f(lhs), rhs)
bool operator()(K lhs, T* rhs) const
return f(lhs, f(rhs))
bool operator()(T* lhs, T* rhs) const
return f(f(lhs), f(rhs))
Comp comp
public:
typedef typename ptrvect<T>::const_iterator iterator, const_iterator
typedef unsigned size_type
typedef T *value_type
typedef K key_type
typedef typename ptrvect<T>::template condpair<iterator> condpair
public:
iterator begin() const
return iterator(impl.begin())
iterator end() const
return iterator(impl.end())
size_type size() const
return impl.size()
bool empty() const
return impl.empty()
public:
iterator lower_bound(K key) const
return std::lower_bound(impl.begin(), impl.end(), key, comp)
iterator upper_bound(K key) const
return std::upper_bound(impl.begin(), impl.end(), key, comp)
std::pair<iterator, iterator> equal_range(K key) const
return std::equal_range(impl.begin(), impl.end(), key, comp)
iterator find(K key) const
iterator it = lower_bound(key)
return it == end() || comp(key, *it) ? end() : it
bool contains(K key) const
iterator it = lower_bound(key)
return it != end() && !comp(key, *it)
public:
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type lower_bound(T* key) const
return std::lower_bound(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type upper_bound(T* key) const
return std::upper_bound(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
std::pair<iterator, iterator> >::type equal_range(T* key) const
return std::equal_range(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type find(T* key) const
iterator it = lower_bound(comp(key))
return it == end() || comp(key, *it) ? end() : it
public:
template<class... Args>
condpair emplace(K key, Args&& ...args)
iterator it = lower_bound(key)
if it == end() || comp(key, *it)
return condpair(impl.insert(it,
new T(key, std::forward<Args>(args)...)), true)
return condpair(it, false)
iterator erase(iterator at)
return impl.erase(at)
public:
T* add(T* value)
iterator it = lower_bound(value)
if it == end() || comp(comp(value), *it)
impl.insert(it, value)
return value
return nullptr
template<class... Args>
T* add(K key, Args&& ...args)
iterator it = lower_bound(key)
if it == end() || comp(key, *it)
T* value = new T(key, std::forward<Args>(args)...)
impl.insert(it, value)
return value
return nullptr
T* get(K key) const
iterator it = find(key)
return it == impl.end() ? nullptr : *it
T* operator[](K key) const
return *emplace(key).first
T* remove(K key)
iterator it = find(key)
if it == impl.end(); return nullptr
T* value = *it
impl.erase(it)
return value
typename std::enable_if<!std::is_same<T*,K>::value,
T*>::type remove(T* key)
return remove(comp(key))
void release()
for T* it : *this
delete it
impl.clear()
void clear()
impl.clear()
You already mentioned c++14 and the template find function. Here is a simple example for who is interested in it:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
class Info
{
int num_;
public:
explicit Info(int n) : num_(n) {}
bool operator<(const Info &other) const { return num_ < other.num_; }
friend inline bool operator<(const Info& lhs, const int& rhs);
friend bool operator<(const int& lhs, const Info& rhs);
};
inline bool operator<(const Info& lhs, const int& rhs) { return lhs.num_ < rhs; }
inline bool operator<(const int& lhs, const Info& rhs) { return lhs < rhs.num_; }
int main()
{
// less<> is a is_transparent comparer
set<Info, less<> > s;
// insert two items
s.insert(Info(2));
s.insert(Info(4));
// search
for (auto n : {1,2,3,4,5}) {
cout << "Searching " << n << (s.find(n) == s.end()?" not found":" found") << endl;
}
return 0;
}
Result:
Searching 1 not found
Searching 2 found
Searching 3 not found
Searching 4 found
Searching 5 not found