OVH Cloud OVH Cloud

pb de tableaux multidimensionnels

11 réponses
Avatar
Marc
Y-a-t-il un modèle dans la STL implémentant les tableaux multidimensionnels,
l'équivalent de vector<> pour les tableaux unidimensionnels ?
Nota : je cherche un modèle pour les tableaux de taille quelconque et
variable et pas seulement bi-dimensionnels, ce qui serait facile à
programmer...Par exemple un modèle où dans le constructeur on passerait un
vector<int> spécifiant les dimensions du tableau. J'ai essayé de m'y mettre
et ça coule pas de source...
Marc

10 réponses

1 2
Avatar
Fabien LE LEZ
On Tue, 2 Nov 2004 21:41:56 +0100, "Marc" :

Par exemple un modèle où dans le constructeur on passerait un
vector<int> spécifiant les dimensions du tableau.


Ça n'existe pas dans la STL, du moins pas directement. Par contre, il
est possible que ce soit dans boost.

Sinon, il est possible d'implémenter ça à partir d'un
std::map < std::vector<int>, T >. Facile à implémenter et très souple,
mais si le tableau est un peu gros et un peu rempli, tu risques de
rencontrer des problèmes de performance.





--
;-)

Avatar
Marc
Merci beaucoup, c'est exactement ce que je cherchais...
J'utilise rarement les map et du coup j'étais parti dans des
vector<vector<..etc..>> , l'horreur sans solution ! alors qu'il y avait si
simple...
Merci encore.
Marc
Avatar
Fabien LE LEZ
On Tue, 2 Nov 2004 21:41:56 +0100, "Marc" :

J'ai essayé de m'y mettre
et ça coule pas de source...


En fait, ça me paraît tout de même assez simple, du moins dans le cas
où la taille du tableau est constante.

On commence par quelques types et "helpers" :

typedef std::vector<int> Coordonnees;
typedef Coordonnees Tailles;
int Produit (Tailles const& tableau); // Renvoie le produit des
éléments de "tableau"

int CalculeOffset (Tailles const& tailles,
Coordonnees const& coord)
{
int offset= 0;
int dimension= tailles.size();
int size_coord= coord.size();

for (int i=0; i<dimension; ++i)
{
offset*= tailles[i];

int coordonnee= (i>=size_coord) ? 0 :

// Vérification optionnelle
if (coordonnee > tailles[i])
throw UneExceptionQuelconque();

offset+= coordonnee;
}
return offset;
}



Première version : on ne s'occupe que des éléments, pas des
sous-tableaux :

template <class T>
class TableauMultiDimensionnel
{
public:
TableauMultiDimensionnel (Tailles const& tailles_, T const& t= T())
: tailles (tailles_)
, data (Produit (tailles_), t)
{}

T& operator () (Coordonnees const& coord)
{
if (tailles.size() != coord.size())
throw UneAutreExceptionQuelconque();
return *&data [CalculeOffset (tailles, coord)];
}

T const& operator () (Coordonnees const& coord) const
{ /* même chose */ }

private:
Tailles tailles;
std::vector<T> data;
};

Et les itérateurs me direz-vous ? En première approximation, ceux de
vector<T> ne devraient pas être trop mal :

template <class T>
class TableauMultiDimensionnel
{
...
typedef std::vector<T>::iterator iterator;
iterator begin() { return data.begin(); }
iterator end() { return data.end(); }
... et la même chose en const
...
};


Deuxième version : on peut créer, en prime, des "sous-tableaux" -- par
exemple, une droite ou un plan dans un espace à trois dimensions.


template <class T> class SousTableau
{
public:
SousTableau (Tailles const& tailles, T* data);
// Implémentation évidente

private:
Tailles tailles;
T* data_externe;
};

class TableauMultiDimensionnel
{
public:
SousTableau GetSousTableau (Coordonnees const& coord)
{
return SousTableau (
Tailles (tailles.begin() + coord.size(), tailles.end(),
&data [CalculeOffset (tailles, coord)];
}
...
};




--
;-)

Avatar
Fabien LE LEZ
On Tue, 02 Nov 2004 23:24:00 +0100, Fabien LE LEZ
:

int coordonnee= (i>=size_coord) ? 0 :


Oups, la ligne est coupée ici. Il fallait lire

int coordonnee= (i>=size_coord) ? 0 : coord[i];


--
;-)

Avatar
Marc
Merci beaucoup pour tes explications détaillées.
Effectivement, je suis arrivé à la même solution que toi quand j'ai voulu
initialiser tous les éléments du tableau. Il m'a fallu déterminer une
"stratégie" de parcours du tableau, et donc en quelque sorte déplier le
tableau. Un simple vector convient alors parfaitement...
Marc
Avatar
Marc
J'ai une question subsidiaire à te poser.
Dans ton constructeur, tu écris
TableauMultiDimensionnel (Tailles const& tailles_, T const& t= T())
et donc tu passes la valeur par défaut par référence, transmise par le
temporaire T()
ça ne pose pas de problème dans ce cas ?
où faut-il mieux écrire
TableauMultiDimensionnel (Tailles const& tailles_, T const t= T())
ce qui crée une copie?
J'avoue que je ne comprends pas toujours où est créé puis détruit l'objet
transmis par référence quand on appelle directement un constructeur dans un
appel de fonction...
Marc
Avatar
Fabien LE LEZ
On Thu, 4 Nov 2004 12:18:25 +0100, "Marc" :

ça ne pose pas de problème dans ce cas ?


Non. Je ne sais plus quel mécanisme est en jeu, mais en pratique, ça
marche :-)
D'ailleurs, regarde le constructeur de std::vector<>...


--
;-)

Avatar
Loïc Joly
Marc wrote:

J'ai une question subsidiaire à te poser.
Dans ton constructeur, tu écris
TableauMultiDimensionnel (Tailles const& tailles_, T const& t= T())
et donc tu passes la valeur par défaut par référence, transmise par le
temporaire T()
ça ne pose pas de problème dans ce cas ?


Petit détail, c'est un passage par référence constante. Autrement, ça ne
serait pas autorisé.

Autrement, la durée de vie de ce temporaire est jusqu'à la fin de
l'évaluation de l'expression, et donc il est valide pendant tout le
temps d'exécution de la fonction.

C'est comme si tu as :
f (int const &i);
f(2+2);

Un entier valant 4 est crée lors de l'évaluation des arguments de la
fonction, et reste valide lors de l'exécution de celle ci, pour être
détruit à la fin de l'évaluation de l'expression contenant un appel de
fonction. Stroustrup discute de designs alternatifs dans le D&E.


où faut-il mieux écrire
TableauMultiDimensionnel (Tailles const& tailles_, T const t= T())
ce qui crée une copie?


Dans ce cas, on crée une copie, ce qui n'est pas forcément utile.

--
Loïc

Avatar
Marc
Merci de tes explications.
J'ai appris une chose importante que j'ignorai et pas évidente a priori...
Je suis allé lire le stroustrup et j'ai lu exactement ce que tu m'expliques
sur les références vers objet constant.
Marc
Avatar
Fabien LE LEZ
On Thu, 4 Nov 2004 12:18:25 +0100, "Marc" :

ça ne pose pas de problème dans ce cas ?


En fait, la méthode canonique pour passer un objet (i.e. autre chose
qu'un type de base) en argument d'une fonction est le passage par
référence constante. Donc il faut que ça marche, même dans le cas où
il y a une valeur par défaut.



--
;-)

1 2