Again indices, iterators and syntax contamination in C++11 (range-based for)
I am implementing some data structures in C++ and thought that some
member functions and operators in the Standard Library are not
necessary.
For example, when do the std::vector size(), max_size(), capacity(),
empty(), pop_back() and push_back(...) member functions are needed? Or
even convenient?
Why iterators and indices in std::vector, when the index can itself be
an iterator for that container?
Why the "sort" algorithm really sorts the contained elements and does
not return a list of iterators in a container?
Often we need the ORDER of the elements in a container, not a sorted
version of the container.
Why ranges are now being proposed and no one discusses containers of
iterators or containers of pointers to data structure nodes? I use
them for lists.
Why the .end() PAST the last element and not a .finish() corresponding
to the last element and 0 (zero) as the sentinel iterator?
This would remove the need for .rbegin(), .rend(), .reverse_iterator,
etc....
Moving forward:
for(auto i=v.start();i!=0;++i)
Moving backward:
for(auto i=v.finish();i!=0;--i)
For example, the "push_back" would simply be:
Vector<double> v;
v.insert(v.finish(),value);
Why the dereferencing operators for iterators? One could use the
access operator [] with an iterator as argument:
std::List::iterator i=List.start();i!=0;++i)
cout<< List[i]<<endl;
or using a small macro with the new C++11 "auto" keyword inside:
#define each(var, container) \
for(auto var = (container).start(); \
var !=0; \
++var)
each(i,List)
cout<<List[i]<<endl;
And finally, a related question. I thought that the .begin()
and .end() convention was a Standard library requirement and not a
syntax requirement. Now, with the range-based for loop, a syntax
ingredient, makes use of a library convention. This puzzles me. I
thought the Standard library made use of the C++ syntax ingredients,
but this looks like a contamination of the syntax by a Standard
library convention.
Why? It seemed that syntax and library should be two different layers
and now the first one is intermixed with the second.
These are **very simple** questions and I am able to cope with a
violent answer. There should be reasons for the way the Standard
library is arranged but I fail to understand them in depth.
And these syntax alterations will produce some confusion. You **have**
to use containers that conform to the Standard library convention to
use a syntax ingredient?
Of course, I should put my effort where my mouth is. I've been
implementing a concise library that shows my idea.
A linked list has, as public member functions:
..clear()
..insertafter(it)
..insertbefore(it)
..remove(it)
..start()
..finish()
..operator[it]
..operator=
As much as I think, maybe only .clear() could be replaced by
remove(it,jt) with it and jt being two iterators. That's it... why so
many public functions, iterators, etc?
As an example, a linked list and test code is attached:
template<typename T> struct linkedlist;
template<typename T> struct linkedindex;
/*
* Linked list node
*/
template<typename T>
struct linkednode
{
public:
/*
* Friends and typedefs
*/
friend struct linkedlist<T>;
friend struct linkedindex<T>;
private:
/*
* Data
*/
linkednode<T>* _previous;
linkednode<T>* _next;
T val;
public:
/*
* Constructors
*/
linkednode(const T& otherval=T())
{
_previous=0;
_next=0;
val=otherval;
}
};
/*
* Basic index
*/
template<typename T>
struct linkedindex
{
public:
/*
* Friends and typedefs
*/
friend struct linkedlist<T>;
private:
/*
* Data
*/
linkednode<T>* node;
public:
/*
* Constructors
*/
linkedindex()
{
node=0;
}
linkedindex(const linkedindex& other)
{
node=other.node;
}
/*
* Comparison operators
*/
bool operator==(const linkedindex& other) const
{
return node==other.node;
}
bool operator!=(const linkedindex& other) const
{
return node!=other.node;
}
/*
* Conversion to void*
*/
operator void* () const
{
return node;
}
/*
* Increment and decrement operators
*/
linkedindex& operator++()
{
node = node->_next;
return *this;
}
linkedindex operator++(int)
{
linkedindex tmp = *this;
operator++();
return tmp;
}
linkedindex& operator--()
{
node = node->_previous;
return *this;
}
linkedindex operator--(int)
{
linkedindex tmp = *this;
operator--();
return tmp;
}
/*
* Attributions
*/
linkedindex& operator=(const linkedindex& other)
{
node=other.node;
return *this;
}
};
/*
* Linked list
*/
template<typename T>
struct linkedlist
{
public:
/*
* Friends and typedefs
*/
friend struct linkednode<T>;
friend struct linkedindex<T>;
typedef linkedindex<T> index;
typedef T type;
private:
/*
* Data
*/
linkednode<T>* _head;
linkednode<T>* _tail;
int _numberofelements;
public:
/*
* Constructors and destructor
*/
linkedlist()
{
_head=0;
_tail=0;
_numberofelements=0;
}
linkedlist(const linkedlist<T>& other)
{
_head=0;
_tail=0;
_numberofelements=0;
for(index it=other.finish(); it!=0; --it)
{
insertbefore(start(),other[it]);
}
}
~linkedlist()
{
clear();
}
private:
/*
* Utility functions
*/
void _insertafter(linkednode<T>* node,linkednode<T>* anode)
{
assert(anode!=0);
_numberofelements++;
if(node==0)
{
assert(_tail==0&&_head==0);
_head=anode;
_tail=anode;
anode->_previous=0;
anode->_next=0;
}
else
{
anode->_previous=node;
anode->_next=node->_next;
if(node->_next==0)
{
_tail=anode;
}
else
{
node->_next->_previous=anode;
}
node->_next=anode;
}
}
void _insertbefore(linkednode<T>* node,linkednode<T>* anode)
{
assert(anode!=0);
_numberofelements++;
if(node==0)
{
assert(_tail==0&&_head==0);
_head=anode;
_tail=anode;
anode->_previous=0;
anode->_next=0;
}
else
{
anode->_previous=node->_previous;
anode->_next=node;
if(node->_previous==0)
{
_head=anode;
}
else
{
node->_previous->_next=anode;
}
node->_previous=anode;
}
}
public:
/*
* Clear the list
*/
void clear()
{
for(index it=start(); it!=0; ++it)
remove(it);
_numberofelements=0;
}
/*
* Indices start and finish
*/
linkedindex<T> start() const
{
linkedindex<T> temp;
temp.node=_head;
return temp;
}
linkedindex<T> finish() const
{
linkedindex<T> temp;
temp.node=_tail;
return temp;
}
/*
* Access operators
*/
T& operator[](linkedindex<T>& it)
{
return it.node->val;
}
const T& operator[](linkedindex<T>& it) const
{
return it.node->val;
}
/*
* Container operations
*/
void remove(const linkedindex<T>& it)
{
linkednode<T>* node=it.node;
assert(node!=0);
_numberofelements--;
if(node->_previous==0)
{
_head=node->_next;
}
else
{
node->_previous->_next=node->_next;
}
if(node->_next==0)
{
_tail=node->_previous;
}
else
{
node->_next->_previous=node->_previous;
}
}
void insertafter(const linkedindex<T>& it,const T& val)
{
_insertafter(it.node,new linkednode<T>(val));
}
void insertbefore(const linkedindex<T>& it,const T& val)
{
_insertbefore(it.node,new linkednode<T>(val));
}
friend ostream& operator<<(ostream& os,linkedlist<T> & v)
{
os<<v._numberofelements<<endl;
for(typename linkedlist<T>::index it=v.start(); it!=0; ++it)
os<<v[it]<<" ";
os<<endl;
return os;
}
friend istream& operator>>(istream& is,linkedlist<T> & v)
{
v.clear();
int dim;
is>>dim;
for(int it=1;it<=dim;++it)
{
T val;
is>>val;
v.insertafter(v.finish(),val);
}
return is;
}
};
int main (int argc, const char * argv[])
{
fstream ofs;
fstream ifs;
linkedlist<double> ld;
ld.insertbefore(ld.start(),333.0);
ld.insertafter(ld.finish(), 345.0);
ld.insertafter(ld.start(), 123.0);
cout<<ld<<endl;
ld.remove(ld.start());
cout<<ld<<endl;
linkedlist<double> wd=ld;
ofs.open("test.txt",fstream::out);
ofs<<wd<<endl;
ofs.close();
ifs.open("test.txt",fstream::in);
linkedlist<double> zd;
ifs>>zd;
cout<<zd<<endl;
cout<<wd<<endl;
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]