Escribiendo su propio contenedor STL

¿Existen pautas sobre cómo se debe escribir un contenedor nuevo que se comporte como cualquier contenedor STL ?

Aquí hay un seudocontenedor de secuencia que compilé del § 23.2.1 \ 4 Tenga en cuenta que iterator_category debe ser uno de std::input_iterator_tag , std::output_iterator_tag , std::forward_iterator_tag , std::random_access_iterator_tag , std::random_access_iterator_tag . También tenga en cuenta que el siguiente es técnicamente más estricto de lo requerido, pero esta es la idea. Tenga en cuenta que la gran mayoría de las funciones “estándar” son técnicamente opcionales, debido a la genialidad de los iteradores.

 template  > class X { public: typedef A allocator_type; typedef typename A::value_type value_type; typedef typename A::reference reference; typedef typename A::const_reference const_reference; typedef typename A::difference_type difference_type; typedef typename A::size_type size_type; class iterator { public: typedef typename A::difference_type difference_type; typedef typename A::value_type value_type; typedef typename A::reference reference; typedef typename A::pointer pointer; typedef std::random_access_iterator_tag iterator_category; //or another tag iterator(); iterator(const iterator&); ~iterator(); iterator& operator=(const iterator&); bool operator==(const iterator&) const; bool operator!=(const iterator&) const; bool operator<(const iterator&) const; //optional bool operator>(const iterator&) const; //optional bool operator<=(const iterator&) const; //optional bool operator>=(const iterator&) const; //optional iterator& operator++(); iterator operator++(int); //optional iterator& operator--(); //optional iterator operator--(int); //optional iterator& operator+=(size_type); //optional iterator operator+(size_type) const; //optional friend iterator operator+(size_type, const iterator&); //optional iterator& operator-=(size_type); //optional iterator operator-(size_type) const; //optional difference_type operator-(iterator) const; //optional reference operator*() const; pointer operator->() const; reference operator[](size_type) const; //optional }; class const_iterator { public: typedef typename A::difference_type difference_type; typedef typename A::value_type value_type; typedef typename const A::reference reference; typedef typename const A::pointer pointer; typedef std::random_access_iterator_tag iterator_category; //or another tag const_iterator (); const_iterator (const const_iterator&); const_iterator (const iterator&); ~const_iterator(); const_iterator& operator=(const const_iterator&); bool operator==(const const_iterator&) const; bool operator!=(const const_iterator&) const; bool operator<(const const_iterator&) const; //optional bool operator>(const const_iterator&) const; //optional bool operator<=(const const_iterator&) const; //optional bool operator>=(const const_iterator&) const; //optional const_iterator& operator++(); const_iterator operator++(int); //optional const_iterator& operator--(); //optional const_iterator operator--(int); //optional const_iterator& operator+=(size_type); //optional const_iterator operator+(size_type) const; //optional friend const_iterator operator+(size_type, const const_iterator&); //optional const_iterator& operator-=(size_type); //optional const_iterator operator-(size_type) const; //optional difference_type operator-(const_iterator) const; //optional reference operator*() const; pointer operator->() const; reference operator[](size_type) const; //optional }; typedef std::reverse_iterator reverse_iterator; //optional typedef std::reverse_iterator const_reverse_iterator; //optional X(); X(const X&); ~X(); X& operator=(const X&); bool operator==(const X&) const; bool operator!=(const X&) const; bool operator<(const X&) const; //optional bool operator>(const X&) const; //optional bool operator<=(const X&) const; //optional bool operator>=(const X&) const; //optional iterator begin(); const_iterator begin() const; const_iterator cbegin() const; iterator end(); const_iterator end() const; const_iterator cend() const; reverse_iterator rbegin(); //optional const_reverse_iterator rbegin() const; //optional const_reverse_iterator crbegin() const; //optional reverse_iterator rend(); //optional const_reverse_iterator rend() const; //optional const_reverse_iterator crend() const; //optional reference front(); //optional const_reference front() const; //optional reference back(); //optional const_reference back() const; //optional template void emplace_front(Args&&...); //optional template void emplace_back(Args&&...); //optional void push_front(const T&); //optional void push_front(T&&); //optional void push_back(const T&); //optional void push_back(T&&); //optional void pop_front(); //optional void pop_back(); //optional reference operator[](size_type); //optional const_reference operator[](size_type) const; //optional reference at(size_type); //optional const_reference at(size_type) const; //optional template iterator emplace(const_iterator, Args&&...); //optional iterator insert(const_iterator, const T&); //optional iterator insert(const_iterator, T&&); //optional iterator insert(const_iterator, size_type, T&); //optional template iterator insert(const_iterator, iter, iter); //optional iterator insert(const_iterator, std::initializer_list); //optional iterator erase(const_iterator); //optional iterator erase(const_iterator, const_iterator); //optional void clear(); //optional template void assign(iter, iter); //optional void assign(std::initializer_list); //optional void assign(size_type, const T&); //optional void swap(X&); size_type size() const; size_type max_size() const; bool empty() const; A get_allocator() const; //optional }; template  > void swap(X&, X&); //optional 

Además, siempre que hago un contenedor, pruebo con una clase más o menos como esta:

 #include  struct verify; class tester { friend verify; static int livecount; const tester* self; public: tester() :self(this) {++livecount;} tester(const tester&) :self(this) {++livecount;} ~tester() {assert(self==this);--livecount;} tester& operator=(const tester& b) { assert(self==this && b.self == &b); return *this; } void cfunction() const {assert(self==this);} void mfunction() {assert(self==this);} }; int tester::livecount=0; struct verify { ~verify() {assert(tester::livecount==0);} }verifier; 

Haga contenedores de objetos de tester y llame a la function() cada uno function() mientras prueba su contenedor. No hagas ningún objeto de tester global. Si su contenedor hace trampas en cualquier lugar, esta clase de tester se assert y sabrá que ha hecho trampas accidentalmente en alguna parte.

Deberá leer la sección Estándar de C ++ sobre los contenedores y los requisitos que el estándar C ++ impone para las implementaciones de contenedores.

El capítulo relevante en el estándar C ++ 03 es:

Sección 23.1 Requisitos del contenedor

El capítulo relevante en el estándar C ++ 11 es:

Sección 23.2 Requisitos del contenedor

El borrador casi final del estándar C ++ 11 está disponible gratuitamente aquí .

También podría leer algunos libros excelentes que lo ayudarán a comprender los requisitos desde la perspectiva del usuario del contenedor. Dos excelentes libros que me han llamado la atención con facilidad son:

STL efectivo por Scott Meyers &
La biblioteca estándar de C ++: un tutorial y referencia de Nicolai Josutils

Aquí hay una implementación muy simplista de un vector falso, que básicamente es un contenedor alrededor de std::vector y tiene su propio iterador (pero real), que imita el iterador STL. De nuevo, el iterador es muy simplista, omitiendo muchos conceptos como const_iterator , verificaciones de validez, etc.

El código se puede ejecutar de fábrica.

 #include  #include  #include  template struct It { std::vector& vec_; int pointer_; It(std::vector& vec) : vec_{vec}, pointer_{0} {} It(std::vector& vec, int size) : vec_{vec}, pointer_{size} {} bool operator!=(const It& other) const { return !(*this == other); } bool operator==(const It& other) const { return pointer_ == other.pointer_; } It& operator++() { ++pointer_; return *this; } T& operator*() const { return vec_.at(pointer_); } }; template struct Vector { std::vector vec_; void push_back(T item) { vec_.push_back(item); }; It begin() { return It(vec_); } It end() { return It(vec_, vec_.size()); } }; int main() { Vector vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); bool first = true; for (It it = vec.begin(); it != vec.end(); ++it) { if (first) //modify container once while iterating { vec.push_back(4); first = false; } std::cout << *it << '\n'; //print it (*it)++; //change it } for (It it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << '\n'; //should see changed value } }