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
};
Related
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
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);
}
I have the following class:
class Data;
class A
{
public:
A(Data& _data) : data(_data) {}
Data& getData() {return data;}
const Data& getData() const {return data;}
private:
Data& data;
};
Now imagine I need to keep not one, but multiple instances of Data. I keep them in a vector of reference wrappers, but I would also like to keep the const correctness: pass the data as unmodifiable in const context.
class A
{
public:
void addData(Data& _data) {data.push_back(std::ref(_data));}
const std::vector<std::reference_wrapper<Data>>& getData() {return data;}
//doesn't compile
//const std::vector<std::reference_wrapper<const Data>>& getData() const {return data;}
private:
std::vector<std::reference_wrapper<Data>> data;
}
How to implement this without having physical copying of the data? I.e. I don't want to return a copy of the vector by value and I don't want to keep two separate vectors in class A. Both are performance-impacting solutions for what is basically just a semantic problem.
Here's a const propagating reference_wrapper, based on cppreference's possible implementation
#include <utility>
#include <functional>
#include <type_traits>
namespace detail {
template <class T> T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<const T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator T& () noexcept { return *_ptr; }
T& get() noexcept { return *_ptr; }
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<T&, ArgTypes...>
operator() ( ArgTypes&&... args ) {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
T* _ptr;
};
template <class T>
class reference_wrapper<const T> {
public:
// types
typedef const T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<const T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<const T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<const T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper<T>& o) noexcept
: _ptr(std::addressof(o.get())) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
const T* _ptr;
};
// deduction guides
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;
You can then add const qualified access via span.
class A
{
public:
void addData(Data& _data) {data.emplace_back(_data);}
std::span<reference_wrapper<Data>> getData() { return { data.data(), data.size() }; }
std::span<const reference_wrapper<Data>> getData() const { return { data.data(), data.size() }; }
private:
std::vector<reference_wrapper<Data>> data;
}
Note that you can't copy or move the const reference_wrapper<Data>s from the second getData, and there is only access to const Data &.
Consider the visitor pattern :
struct ConstVisitor {
virtual ~ConstVisitor() = default;
virtual bool visit(const Data & data) = 0;//returns true if search should keep going on
};
void A::accept(ConstVisitor & visitor) const;
This way it does not matter to the outside world what kind of container Data is stored in (here a std::vector). The visitor pattern is very similar to an Enumerator in C#.
I have the following code:
template <class T>
class List {
public:
class Iterator;
class ConstIterator;
//Constructors and Destructors.
List() : head(NULL), tail(NULL), size(0) {}
List(const List& list);
~List();
//Methods
Iterator begin();
Iterator end();
void insert(const T& data);
void insert(const T& data, const Iterator& iterator);
void remove(const Iterator& iterator);
int getSize() const;
Iterator find();
void sort();
//Operators
List operator = (const List& list);
private:
class Node;
Node* head;
Node* tail;
int size;
};
template <class T>
class List<T>::Node
{
public:
//Constructors and destructors
Node(const T& _data, const Node* _next) : data(_data), next(_next) {}
~Node(); //Destructor
//Methods
//Operators
Node operator = (const Node& node);
private:
T data;
Node* next;
};
template<class T>
class List<T>::Iterator
{
public:
Iterator() : list(NULL), node(NULL){} //Constructor
Iterator(const Iterator& it) : list(it.list), node(it.node) {}
~Iterator(); //Destructor
Iterator& operator=(const Iterator& it);
T& operator * ();
T& operator ++ ();
T operator ++ (int);
T& operator -- ();
T operator -- (int);
bool operator == (const Iterator& iterator) const;
bool operator != (const Iterator& iterator) const;
private:
List<T>* list;
Node* node;
};
template<class T>
class List<T>::ConstIterator
{
public:
ConstIterator() : list(NULL), node(NULL){}
ConstIterator(const ConstIterator& it) : list(it.list), node(it.node) {}
~ConstIterator(); //Destructor
ConstIterator& operator=(const ConstIterator& it);
T& operator * ();
T& operator ++ ();
T operator ++ (int);
T& operator -- ();
T operator -- (int);
bool operator == (const ConstIterator& iterator) const;
bool operator != (const ConstIterator& iterator) const;
private:
const List<T>* list;
const Node* node;
};
template<class T>
Iterator List<T>::begin() {
return Iterator(this, head);
}
When I try to compile I get the following error:
error: expected constructor, destructor, or type conversion before ‘List’
On line:
Iterator List<T>::begin() {
I'm not sure what I'm doing wrong.
Iterator is not defined, but List<T>::Iterator is. You will also need to add typename:
template<class T>
typename List<T>::Iterator List<T>::begin() { ... };
Here, typename is required as an ambiguitator to tell the compiler that List<T>::Iterator is a type (rather than a static member). This is always required in the templated context (see here).
if you write the body of the function outside the class declaration, it should be:
typename List<T>::Iterator List<T>::begin() { ... }
edit: typename added
Here is the .hpp file:
template<typename T>
LinkedQueue<T> operator=(const LinkedQueue<T> & lhs, const LinkedQueue<T> & rhs)
{
m_data = rhs.m_data;
m_next = rhs.m_next;
}
The error says that first line must be a nonstatic member function. Here is the class it is in:
template<typename T>
class LinkedQueue:public AbstractQueue<T>
{
public:
T m_data;
LinkedQueue *m_next;
LinkedQueue<T> operator=(const LinkedQueue<T> & rhs);
LinkedQueue();
void clear();
void enqueue(T x);
void dequeue();
const T& front() const;
bool isEmpty() const;
};
Any idea as to what silly thing I am doing wrong?
You should add a class qualifier to the function definition, and remove the unused lhs parameter:
template<typename T>
LinkedQueue<T>& LinkedQueue::operator=(const LinkedQueue<T> & rhs)
// ^--- & should be added to the declaration, too
{
m_data = rhs.m_data;
m_next = rhs.m_next;
return *this;
}
you should write look like this;
template<typename T>
class LinkedQueue:public AbstractQueue<T>
{
public:
T m_data;
LinkedQueue *m_next;
LinkedQueue<T> & operator=(const LinkedQueue<T> & rhs)
{
if (this != &rhs)
{
m_data = rhs.m_data;
m_next = rhs.m_next;
}
return *this;
}
LinkedQueue();
void clear();
void enqueue(T x);
void dequeue();
const T& front() const;
bool isEmpty() const;
};