I am trying to learn C++ and would like to understand iterators better. To that end, I tried to write an STL compatible iterator for a custom class (called StrBlob which holds strings, defined in the wonderful C++ Primer). Eventually, the following iterator definition worked:
class StrBlob::iterator
: public std::iterator<std::input_iterator_tag, // iterator_category
std::string, // value_type
std::string, // difference_type
const std::string*, // pointer
std::string // reference
> {
public:
iterator() : curr(0) {}
iterator(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
iterator& operator++(); // prefix operators
iterator& operator--();
iterator operator++(int); // postfix operators
iterator operator--(int);
std::string& operator*() const;
std::string* operator->() const;
bool operator==(const iterator& b) const { return curr == b.curr; }
bool operator!=(const iterator& b) const { return curr != b.curr; }
private:
std::shared_ptr<std::vector<std::string>> check(std::size_t,
const std::string&) const;
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr;
};
Initially I tried for a long time in vain to design the iterator with the right type definitions myself. This didn't work, so I simply provided an inheritance to std::iterator as mentioned in cppreference.
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
(I tried to understand Stackoverflow answers to related questions about iterator design but they were a bit to abstract for me.)
Your code is bugged, std::string is not a suitable difference type (difference_type is the difference between two iterators so normally its std::ptrdiff_t), nor is std::string a suitable reference type (should be std::string&).
Here's the correct typedefs, you can use these instead of inheritance.
typedef std::input_iterator_tag iterator_category;
typedef std::string value_type;
typedef std::ptrdiff_t difference_type;
typedef std::string* pointer;
typedef std::string& reference;
Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?
Yes, std::iterator (deprecated in C++17), only provides typedef.
So you can add those typedef yourself instead of using inheritance.
class StrBlob::iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = std::string;
using difference_type = std::string;// should be `std::ptrdiff_t`
using pointer = const std::string*; // const should probably be dropped, unless you do const_iterator
using reference = std::string; // should be std::string&
// ...
};
Related
the code below is an adoption from here and implements a user-defined random access iterator for a class holding a dynamic array in a shared pointer. In member function any the std::any_if, which requires an input_iterator, is called. From my knowledge and comments here using a random_access_iterator instead of an input_iterator should be perfectly legal. Unfortunately it does not compile with g++ and clang++ with the error message:
In file included from iterator.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/iostream:39:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/ostream:38:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/ios:40:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/bits/char_traits.h:39:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/bits/stl_algobase.h:2065:25: error: invalid operands to binary expression ('xx<long long>::Iterator' and 'xx<long long>::Iterator')
__trip_count = (__last - __first) >> 2;
It compiles when the iterator category is changed to input_iterator.
Any ideas about the root of the problem are highly appreciated.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <memory>
using namespace std;
template <typename T>
class xx{
struct Iterator
{
using iterator_category = std::random_access_iterator_tag;
//using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
Iterator(pointer ptr) : m_ptr(ptr) {}
reference operator*() const { return *m_ptr; }
pointer operator->() { return m_ptr; }
Iterator& operator++() { m_ptr++; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; };
friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_ptr != b.m_ptr; };
private:
pointer m_ptr;
};
protected:
std::shared_ptr<T[]> m_data;
int n=0;
public:
xx()=default;
xx(int n):n{n}{m_data=shared_ptr<T[]>(new T[n]);};
~xx(){};
Iterator begin() { return Iterator(&m_data.get()[0]); }
Iterator end() { return Iterator(&m_data.get()[n]); }
const int sz()const{return(n);}
void any(xx<long long int> &id){
if(any_of(id.begin(),id.end(),[this](long long int i){return(i<0 || i>(this->sz()-1));})){
std::string msg="error";throw msg;
}
}
};
int main(){
xx<double> x(10);int i=0;
xx<long long int> y(5);
cout<<x.sz()<<endl;
for(auto s=x.begin();s!=x.end();++s){
*s=(double)i++;
}
i=0;
for(auto s=y.begin();s!=y.end();++s){
*s=i++;
}
for(auto i : x){
cout<<i<<endl;
}
x.any(y);
return(0);
}
A random access iterator is one that (a) has the random access iterator tag, and (b) fullfills the requirements of being a random access iterator.
Yours fails on (b).
You need to implement [] - and + and related and < etc. And obey the other rules and requirements with them.
The tag determines which implementation your iterator is dispatched to; yours fails to provide expected features for the iterator category you claim to have, so it breaks.
The standard states that lying about your iterator category means passing your iterator in makes your program ill formed, no diagnostic required. In this case, an optimized implementation that works faster on random access iterators exists, but breaks on your lie.
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.
I want to know how to have a c++ class to be iterable (stl compatible) without exposing the implementation ?
The structure of the project is like :
Stream
class Stream
{
public:
Stream();
[...]
StreamIterator iter()
{
return StreamIterator(this);
}
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter
class StreamFilter
{
public:
StreamFilter();
[...]
private:
class impl;
std::shared_ptr<impl> pimpl;
};
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter is a base class for differents filtering strategies.
For simplicity in the sample code, I used raw memory pointers, of course in a real exploitation code I use intelligents pointers : shared, weak ...
I want to allow the StreamIterator become iterable in a STL way, doing :
StreamIterator iter = stream.iter();
iter.addFilter(new FilterByOffset([...with parameters...]));
for (auto item : iter)
{
[...doing something with filtered items...]
}
The basic way is to add some accessors to allow range-based for loop.
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
Where iterator and const_iterator are basically typedef's to the internal container iterators. And this is the problem.
First, I don't want to expose private implementation in the StreamIterator header. And so, iterator and const_iterator are not allowed here.
Second, because of the stream filtering strategy, the iterators returned are not just alias to some internal stl containers. In the internal implementation, I need to call the filters in a functor way to check if the item need to be exclude or not.
The only type allowed in the StreamIterator header is the type of the item object returned.
Is there a way to do that?
Thank you very much!
Additional Information:
Maybe this declaration is a way to allow a private implementation, I need to investigate more :
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
struct iterator
{
Object::Ptr operator*();
iterator& operator++();
bool operator!= (const iterator& it) const;
};
typedef typename StreamIterator::iterator iterator;
iterator begin();
iterator end();
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
First, don't call it StreamIterator; an iterator is a pointer-like object for which 2 of them can specify a range. Your StreamIterator doesn't have this. Nothing good can come of reusing the well defined iterator term here.
Now, your StreamIterator is some kind of range of iterators. So we'll call it a StreamRange.
In order to hide how StreamRange can be iterated over, you have to hide how the iterators it uses work.
And this -- hiding the implementation detalis of a stream iterator -- has a substantial cost to it.
When iterating over a loop, each step in the loop involves ++ and * and an ==. Throwing a pointer indirection and a vtable lookup (or equivalent) on each of those will make your loops much slower.
But here is how to do it.
template<class Value>
struct any_input_iterator {
using difference_type = std::ptrdiff_t;
using value_type=Value;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
private:
struct vtable_t {
bool(*equal)( std::any const& lhs, std::any const& rhs ) = 0;
Value(*get)( std::any& ) = 0;
void(*inc)( std::any& ) = 0;
};
vtable_t const* vtable = 0;
std::any state;
template<class U>
static vtable_t make_vtable() {
return {
[](std::any const& lhs, std::any const& rhs)->bool {
return std::any_cast<U const&>(lhs) == std::any_cast<U const&>(rhs);
},
[](std::any& src)->Value {
return *std::any_cast<U&>(src);
},
[](std::any& src) {
++std::any_cast<U&>(src);
}
};
}
template<class U>
static vtable_t const* get_vtable() {
static const auto vtable = make_vtable<U>();
return &vtable;
}
public:
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, any_input_iterator>{}, bool> = true
>
any_input_iterator(U&& u):
vtable(get_vtable<std::decay_t<U>>()),
state(std::forward<U>(u))
{}
any_input_iterator& operator++() { vtable->inc(state); return *this; }
any_input_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; }
reference operator*() { return vtable->get(state); }
friend bool operator==( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
if (lhs.vtable != rhs.vtable) return false;
if (!lhs.vtable) return true;
return lhs.vtable->equal( lhs.state, rhs.state );
}
friend bool operator!=( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
return !(lhs==rhs);
}
struct fake_ptr {
Value t;
Value* operator->()&&{ return std::addressof(t); }
};
fake_ptr operator->()const { return {**this}; }
};
there are probably some typoes, but this is basic type erasure. Boost does a better job at this.
I only supported input iterators. If you want to support forward iterators, you have to change up some typedefs and return references and the like.
any_input_iterator<int> begin();
any_input_iterator<int> end();
that is, however, enough to let someone iterate over your range in question.
It will be slow, but it will work.
I've never worked with iterators before, and I'm having trouble designing a custom iterator for a custom container class that I wrote.
Background: Using Google Tests API, these are the two test cases that I have:
TEST(RandomArray, End) {
RandomArray r(17);
int *b = r.begin();
int *e = r.end();
EXPECT_EQ(b + 17, e);
}
TEST(RandomArray, IteratorTypedef) {
RandomArray r(7);
for (RandomArray::iterator it = r.begin(); it != r.end(); ++it) {
*it = 89;
EXPECT_EQ(89, *it);
}
}
Here's my header file and the code for the iterators:
class RandomArray
{
friend ostream& operator<<(ostream&, const RandomArray&);
public:
class iterator
{
public:
typedef iterator self_type;
typedef int* pointer;
typedef int& reference;
self_type operator++() { self_type i = *this; ptr++; return i;}
reference operator*() {return *ptr;}
bool operator!=(const self_type& rhs) {return ptr != rhs.ptr;}
private:
pointer ptr;
};
class const_iterator
{
public:
typedef const_iterator self_type;
typedef int* pointer;
typedef int& reference;
self_type operator++() { self_type i = *this; ptr++; return i;}
const reference operator*() { return *ptr; }
bool operator!=(const self_type& rhs) {return ptr != rhs.ptr;}
private:
pointer ptr;
};
RandomArray();
RandomArray(size_t);
size_t size() const;
int* begin();
iterator begin();
const int* begin() const;
const iterator begin() const;
int* end();
iterator end();
const int* end() const;
const iterator end() const;
private:
size_t capacity;
int* data;
};
The error I'm getting on begin and end is the following: Error: Cannot overload functions distinguished by return type alone.
I know you're not allowed to have the same function name and same parameters with different return types, so I'm wondering if there's a better way to do this? Am I making the iterator right? Would a template help fix this? I need begin() and end() to return both an int* and an iterator so I can pass both test cases. Is there a better way to accomplish this?
I need begin() and end() to return both an int* and an iterator so I can pass both test cases.
No, you don't. The test case expecting a pointer is wrong. Containers give you back iterators. In your case, your iterator could be a pointer, but that's an implementation detail. You definitely just want:
iterator begin();
const_iterator begin() const; // NB: const_iterator, not const iterator
And then fix your unit test to expect RandomArray::iterator instead of int*. Or, even better, auto.
Note: Your operator++() does postfix increment instead of prefix increment. Also const reference is the wrong type, that woudl be int& const, and references are inherently const. You want to change your typedef for reference to itself be int const&.
If you can't change the test case with int *b = r.begin(); (though if this is a new class, why not?) then you need to either make your iterator type be the pointer type, or be convertible to the pointer type.
In the first case, get rid of the iterator classes and write using iterator = int*; using const_iterator = int const*;.
In the second case, add conversion functions operator pointer() const { return ptr; }.
The second case is better, as this allows you to deprecate the conversion function in future. However it would be even better to fix the test case to use the iterator type.
I figured out what I was doing wrong. All I had to do was declare a typedef iterator as an int*.
Updated Code:
class MyClass
{
public:
int* begin();
const int* begin() const;
int* end();
const int* end() const;
//HERE
typedef int* iterator;
typedef const int* const_iterator;
private:
size_t capacity;
int* data;
};
Then, in the function body I changed it to this:
int* MyClass::begin()
{
return iterator(&data[0]);
}
const int* MyClass::begin() const
{
return const_iterator(&data[0]);
}
int* MyClass::end()
{
return iterator(&data[capacity]);
}
const int* MyClass::end() const
{
return const_iterator(&data[capacity]);
}
I have a class that holds a list containing boost::shared_ptrs to objects of another class.
The class member functions that give access to the elemets in the list return raw pointers. For consistency I'd also like to be able to iterate with raw pointers instead of shared_ptrs. So when I dereference the list iterator, I'd like to get raw pointer, not a shared_ptr.
I assume I need to write a custom iterator for this. Is this correct? If so can someone point me in the right direction - I've never done this before.
Here's an option using Boost transform_iterator:
#include <list>
#include <boost/iterator/transform_iterator.hpp>
#include <tr1/memory>
#include <tr1/functional>
using std::list;
using std::tr1::shared_ptr;
using boost::transform_iterator;
using boost::make_transform_iterator;
using std::tr1::mem_fn;
using std::tr1::function;
struct Foo {};
struct Bar
{
typedef shared_ptr< Foo > Ptr;
typedef list< Ptr > List;
typedef function< Foo* (Ptr) > Functor;
typedef transform_iterator< Functor, List::iterator > Iterator;
Iterator begin()
{
return make_transform_iterator( fooptrs.begin(), mem_fn( &Ptr::get ) );
}
Iterator end()
{
return make_transform_iterator( fooptrs.end(), mem_fn( &Ptr::get ) );
}
List fooptrs;
};
C++11 would make it easy to eliminate the function wrapper but I don't have a compiler handy to test it out. You could also hide the concrete type of Iterator using type-erasure if you saw the need (I think Adobe offers a free any_iterator class template for this purpose.)
I sometimes see people reaching for STL containers of boost::shared_ptr when actually the less obvious and relatively little known boost::ptr_container might be a better choice.
This may or may not be one of those cases, but consider that one of the nice properties of the ptr_container classes is that their iterators have an "extra" indirection which helps keep things clean and safe.
🧟♀️ ... zombie thread ... 🧟
If you can't use Boost (e.g., it's part of your public interface and you don't want to make that a requirement for your users), you can roll your own relatively easily. For a container my_container -- which could be a front for a standard, boost, whatever container but whose actual implementation is hidden in your implementation files and not exposed to your API -- holding any smart pointer to a class my_type, it would be something like:
class iterator
{
public:
~iterator();
iterator( const iterator& other );
iterator& operator=( const iterator& other );
// The standard iterator traits (names must be lower-case)
typedef std::forward_iterator_tag iterator_category;
typedef my_type* value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type reference;
iterator& operator++();
iterator& operator++( int );
reference operator*() const;
friend bool operator==( const iterator& it1, const iterator& it2 );
friend bool operator!=( const iterator& it1, const iterator& it2 );
private:
// Private, type-erased construction
friend class my_container;
explicit iterator( void* );
// Implementation hidden by pimpl
struct impl;
impl* _impl;
};
And in the cpp file, assuming we're using a vector of shared_ptr under the hood:
// Define the Pimpl struct
struct iterator::impl
{
typedef std::vector< std::shared_ptr<my_type> >::iterator iterator;
iterator iter;
};
// Use void* as type erasure to hide the actual types from the user
iterator::iterator( void* wrappedIter )
: _impl( new impl( *reinterpret_cast<impl::iterator*>( wrappedIter ) ) )
{
}
// Copying
iterator::iterator( const iterator& other )
: _impl( new impl( *other._impl ) )
{}
iterator& iterator::operator=( const iterator& other )
{
_impl->iter = other._impl->iter;
return *this;
}
// ... could implement moving too ...
iterator::~iterator()
{
// Destroy the pimpl
delete _impl;
}
iterator& iterator::operator++()
{
++_impl->iter;
return *this;
}
iterator& iterator::operator++( int )
{
++_impl->iter;
return *this;
}
iterator::reference iterator::operator*() const
{
// This is where the magic happens: We convert the shared_ptr to a raw pointer.
return _impl->iter->second.get();
}
bool operator==( const iterator& it1, const iterator& it2 )
{
return *it1 == *it2;
}
bool operator!=( const iterator& it1, const iterator& it2 )
{
return !( it1 == it2 );
}
Then all you need is a way to create these. To that end my_container's begin()/end() functions would look like:
my_container::iterator my_container::begin()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.begin();
return iterator( &iter );
}
my_container::iterator my_container::end()
{
// The & allows our type erasure by invoking our void* constructor
auto iter = _data->vector.end();
return iterator( &iter );
}
That type erasure and taking the address of a member variable is a little dicey looking, but it's okay since we're immediately reinterpreting the void* and dereferencing and copying it.