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?