Tableau dynamique – Implémentation collaborative avec std::function

Similaire à la stratégie collaborative et générique, l'approche proposée ici suggère de poser la question différemment et d'adjoindre simplement une fonction à notre tableau dynamique. Cette fonction, modélisée par un std::function, retournera simplement la nouvelle capacité à utiliser, étant donné une capacité existante.

C'est à la fois léger et efficace. Voir https://wandbox.org/permlink/vEuiwjA3B8PZhZGn :

#ifndef TABLEAU_H
#define TABLEAU_H
 
#include <algorithm>
#include <utility>
#include <initializer_list>
#include <functional>

class HorsBornes {};

template <class T>
   class Tableau {
   public:
      // using ...
   private:
      static auto croissance_defaut() {
         return [](size_type pre) -> size_type {
            return pre? pre * 2 : 128;
         };
      }
      std::function<size_type(size_type)> croisseur { croissance_defaut() }; // <-- ICI
      pointer elems {};
      size_type nelems {},
                cap {};
   public:
      // empty(), size(), capacity(), full()
      // begin(), cbegin(), end(), cend()
      // constructeur par défaut
      //
      // constructeur paramétrique
      //
      Tableau(size_type n, const_reference val, std::function<size_type(size_type)> strategie = croissance_defaut()) // <-- ICI
         : nelems{ n }, elems{ new value_type[n] }, cap{ n }, croisseur{ strategie } {
         try {
            std::fill(begin(), end(), val);
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur de copie
      //
      Tableau(const Tableau &autre)
         : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() }, croisseur{ autre.croisseur } {
         try {
            std::copy(autre.begin(), autre.end(), begin());
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur par liste d'initialisation
      //
      Tableau(std::initializer_list<value_type> src, std::function<size_type(size_type)> strategie = croissance_defaut())
         : nelems{ src.size() }, cap{ src.size() }, elems{ new value_type[src.size()] }, croisseur{ strategie } {
         try {
            std::copy(src.begin(), src.end(), begin());
         } catch(...) {
            delete [] elems;
            throw;
         }
      }
      //
      // constructeur de séquence (pourrait bénéficier de enable_if ou
      // des concepts de C++ 20)
      //
      template <class It>
         Tableau(It debut, It fin, std::function<size_type(size_type)> strategie = croissance_defaut())
            : nelems{ std::distance(debut, fin) }, croisseur{ strategie } {
            cap = size();
            elems = new value_type[capacity()];
            try {
               std::copy(debut, fin, begin());
            } catch(...) {
               delete [] elems;
               throw;
            }
         }
      //
      // constructeur de conversion (signature qui mérite réflexion)
      //
      template <class U>
         Tableau(const Tableau<U> &autre)
            : nelems{ autre.size() }, elems{ new value_type[autre.size()] }, cap{ autre.size() }, croisseur{ croissance_defaut() } {
            try {
               std::copy(autre.begin(), autre.end(), begin());
            } catch(...) {
               delete [] elems;
               throw;
            }
         }
      //
      // constructeur de mouvement
      //
      Tableau(Tableau &&autre)
         : elems { std::exchange(autre.elems, nullptr) },
           nelems { std::exchange(autre.nelems, 0) },
           cap { std::exchange(autre.cap, 0) },
           croisseur { std::move(autre.croisseur) } {
      }
      ~Tableau() {
         delete [] elems;
      }
      // swap(), affectation, affectation covariante
      //
      // affectation par mouvement
      //
   private:
      void grow() {
         const auto nouv_cap = croisseur(capacity());
         auto p = new value_type[nouv_cap];
         try {
            std::copy(begin(), end(), p);
            delete [] elems;
            elems = p;
            cap = nouv_cap;
         } catch(...) {
            delete [] p;
            throw;
         }
      }
   public:
      // push_back(), at(), operator[]
      // operator==, operator!=
   };
 
#endif

Un programme de test suit. Ce programme ajoute N éléments au tableau, provoquant au passsage à plus d'une reprise l'activation de la stratégie de croissance, puis affiche les éléments sur la sortie standard.

#include "Tableau.h"
#include <iostream>
int main() {
   using namespace std;
   enum { N = 1'000 };
   Tableau<int> tab;
   for (int i = 0; i < N; ++i)
      tab.push_back(i);
   for(auto n : tab) cout << n << ' ';
   cout << endl;
}

Que pensez-vous cette solution?


Valid XHTML 1.0 Transitional

CSS Valide !