Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

vector de classes template

10 réponses
Avatar
btzaf
Hello,

J'ai une classe Element :
template<class T>
class Element
{
...
}

Comment créer un std::vector d'Element ?

J'ai essayé pas mal de variations autour de
std::vector< Element <class T> > tableau;
ou
std::vector< Element > tableau;
ou ....

sans succès. Rien trouvé non plus ni dans la C++ FAQ Lite ni dans le
"mega-cours" (ou alors je suis miro ce qui n'est pas à exclure).

Merci d'avance à qui me donnera une piste

btz

10 réponses

Avatar
Sylvain Togni

J'ai une classe Element :
template<class T>
class Element
{
...
}

Comment créer un std::vector d'Element ?


Ce n'est pas possible, Element n'est pas une classe, c'est
un template, c'est à dire un patron pour générer automatiquement
des classes.

Par contre Element<int> ou Element<double> ou Element<T>
(quelque soit le type T) sont des classes et peuvent être
utilisées dans un std::vector.

--
Sylvain Togni

Avatar
Sylvain
btzaf wrote on 17/10/2007 17:17:
Hello,

J'ai une classe Element :
template<class T>
class Element
{
}

Comment créer un std::vector d'Element ?

J'ai essayé pas mal de variations autour de
std::vector< Element <class T> > tableau;
ou
std::vector< Element > tableau;

sans succès. [...]


instancier, comme vous le faites, un variable donnée ("tableau") comme
vecteur d'élément paramétré est bien sur possible pour peu que vous
désignez la classe paramètre:

std::vector< Element <class T> > tableau;

devient:

// quelque chose valant pour paramètre
class Item {
...
};

std::vector< Element<Item> > tableau;

et je pense que votre intention est surement cette finalité (création
d'un vecteur d'instances paramétrés par une classe concrête) plutôt que
de créer une variable "vecteur d'instances paramétrables".

cette seconde intention serait inattendue pour une variable donnée et ne
se concevrait que pour créer un type paramétré (une classe template)
dérivé de vector gérant des Elements eux même paramétrés - ceci est hors
de mes très faible connaissance en template, peut être est-ce non
réalisable, peut-être est-ce seulement couteux en <>

Sylvain.

Avatar
btzaf
btzaf wrote on 17/10/2007 17:17:
Hello,

J'ai une classe Element :
template<class T>
class Element
{
}

Comment créer un std::vector d'Element ?


instancier, comme vous le faites, un variable donnée ("tableau") comme
vecteur d'élément paramétré est bien sur possible pour peu que vous
désignez la classe paramètre:

[...]

et je pense que votre intention est surement cette finalité (création
d'un vecteur d'instances paramétrés par une classe concrête) plutôt que
de créer une variable "vecteur d'instances paramétrables".

cette seconde intention serait inattendue pour une variable donnée et ne
se concevrait que pour créer un type paramétré (une classe template)
dérivé de vector gérant des Elements eux même paramétrés - ceci est hors
de mes très faible connaissance en template, peut être est-ce non
réalisable, peut-être est-ce seulement couteux en <>


Merci Sylvain(s ?) pour vos réponses.

En voulant simplifier mon problème à outrance pour plus de clarté je me
suis mal exprimé. Je comprends bien qu'on ne puisse écrire :
std::vector< Element <class T> > tableau;
et je n'en ai d'ailleurs pas besoin.

De fait, ce que je veux c'est permettre à une méthode d'une autre classe
(appelons-là Foo) de recevoir un vecteur d'Element :

template<class T>
Foo::bar(std::vector< Element <T> >& tableau)

ce qui me permettrait d'avoir des choses comme

Element<int> eint1, eint2;
std::vector<Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo foo;
foo.bar(vec);

Seulement ce code ne compile pas, le compilo me dit:

vec-template.cpp: In function ‘int main()’:

vec-template.cpp:50: error: no matching function for call to
‘Foo<Element<int> >::bar(std::vector<Element<int>,
std::allocator<Element<int> > >&)’

vec-template.cpp:31: note: candidates are:
Foo<T>::bar(std::vector<Element<T>, std::allocator<Element<T> > >&)
[with T = Element<int>]

vec-template.cpp:28: note: Foo<Element<int> >::bar(const
Foo<Element<int> >&)

De manière générale, ce que je me demande, donc, c'est s'il est possible
de pousser le mécanisme des templates de telle sorte que nous ayons des
classes templates dont les méthodes reçoivent elles-mêmes des instances
de classes templates et celà sans avoir à surcharger ces méthodes pour
chaque type possible de la classe template paramètre, soit pouvoir
écrire des définitions :

template<class T>
Foo::bar(std::vector< Element <T> >& vec)

plutôt que :

Foo::bar(std::vector< Element <int> >& vec) { ... }
Foo::bar(std::vector< Element <float> >& vec) { ... }

dont je me rends compte en l'écrivant qu'elles ne résoudraient
d'ailleurs pas mon problème car in-fine je voudrais récupérer 'vec'
dans une variable d'instance 'Foo::_vec' dont je ne vois pas de quel
type elle pourrait être sauf à écrire un truc de genre
std::vector< Element<T> > _vec;
:-/


Avatar
Michel Decima

De fait, ce que je veux c'est permettre à une méthode d'une autre classe
(appelons-là Foo) de recevoir un vecteur d'Element :

template<class T>
Foo::bar(std::vector< Element <T> >& tableau)

ce qui me permettrait d'avoir des choses comme

Element<int> eint1, eint2;
std::vector<Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo foo;
foo.bar(vec);

Seulement ce code ne compile pas, le compilo me dit:


Pourtant, ca devrait. Quel compilateur/version ?

Avatar
btzaf

De fait, ce que je veux c'est permettre à une méthode d'une autre classe
(appelons-là Foo) de recevoir un vecteur d'Element :

template<class T>
Foo::bar(std::vector< Element <T> >& tableau)

ce qui me permettrait d'avoir des choses comme

Element<int> eint1, eint2;
std::vector<Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo foo;
foo.bar(vec);

Seulement ce code ne compile pas, le compilo me dit:


Pourtant, ca devrait. Quel compilateur/version ?


g++ (GCC) 4.2.1

Pour info, voilà le code test

8<--- vec-template.cpp
#include <vector>

// Element
template<class T>
class Element
{
T _value;
public:
Element(T value) : _value(value) {}
T getValue (void ) const { return _value; }
};

// Foo
template<class T>
class Foo
{
std::vector< Element<T> > _vec;
public:
void bar (std::vector< Element<T> >& vec) { _vec = vec; }
};

int main(void)
{
Element<int> eint1(25);
Element<int> eint2(34);
std::vector< Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo<Element<int> > foo;
foo.bar(vec);
}

8<---

et le résultat de la compilation

8<---

# g++ vec-template.cpp
vec-template.cpp: In function ‘int main()’:
vec-template.cpp:30: error: no matching function for call to
‘Foo<Element<int> >::bar(std::vector<Element<int>,
std::allocator<Element<int> > >&)’
vec-template.cpp:19: note: candidates are: void
Foo<T>::bar(std::vector<Element<T>, std::allocator<Element<T> > >&)
[with T = Element<int>]

# g++ --version
g++ (GCC) 4.2.1 (Debian 4.2.1-3)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

8<---


Avatar
James Kanze
On Oct 18, 9:31 am, btzaf wrote:

De fait, ce que je veux c'est permettre à une méthode d'une
autre classe (appelons-là Foo) de recevoir un vecteur
d'Element :

template<class T>
Foo::bar(std::vector< Element <T> >& tableau)

ce qui me permettrait d'avoir des choses comme

Element<int> eint1, eint2;
std::vector<Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo foo;
foo.bar(vec);

Seulement ce code ne compile pas,


Je crois que c'est une contexte « non-deduced ». J'ai beaucoup
de mal à comprendre l'anglais de la norme ici -- aussi, la
texte dans la proposition la plus récente diffère assez de celui
de la norme 1998 --, mais la norme dit :

The nondeduced contexts are:

[...]

-- A type that is a template-id in which one or more of
the template-arguments is an expression that
references a template-parameter.

(version 1998), ou :

The nondeduced contexts aer:

[...]

-- A template parameter used in the parameter type of a
function parameter that has a default argument that
is being used in the call for which argument
deduction is being done.

(« latest draft »).

Supérficiellement, ces phrases ressemblent à l'anglais, mais
c'est un anglais trop complexe pour que je le comprenne (et mon
anglais, en général, n'est pas si mal que ça). Mais j'ai
l'impression que dans les deux cas (la norme actuelle et la
derniére proposition), tu te trouves bien dans un cas où la
déduction ne fonctionne pas.

La solution la plus simple, peut-être, c'est d'écrire quelque
chose du genre :

template< typename Container >
void
Foo::bar( Container& tableau ) { /* ... */ }

Ensuite, tu documentes que c'est un comportement indéfini si
Container n'est pas un std::vector< Element< T > >. Tu pourrais
même y ajouter des contraintes ; quelque chose du genre :

template< typename T >
void
testTypeIsElement( Element< T > const& ) {}

template< typename Container >
void
Foo::bar( Container& tableau ) {
testTypeIsElement( Container::value_type() ) ;
// ...
}

De tête, je ne vois pas de moyen à contraindre que Container
soit une instance de std::vector. Mais est-ce que c'est
important, du moment qu'il supporte toutes les opérations du
vector qui m'interesse ? D'ailleurs, il se peut bien qu'un tel
test n'est pas nécessaire. Si, par exemple, tu utilises
autrement des opérations sur Container::value_type qui ne sont
valides que sur un Element<T>.

le compilo me dit:

vec-template.cpp: In function ?int main()?:

vec-template.cpp:50: error: no matching function for call to
?Foo<Element<int> >::bar(std::vector<Element<int>,
std::allocator<Element<int> > >&)?

vec-template.cpp:31: note: candidates are:
Foo<T>::bar(std::vector<Element<T>, std::allocator<Element<T> > >&)
[with T = Element<int>]

vec-template.cpp:28: note: Foo<Element<int> >::bar(const
Foo<Element<int> >&)

De manière générale, ce que je me demande, donc, c'est s'il est pos sible
de pousser le mécanisme des templates de telle sorte que nous ayons des
classes templates dont les méthodes reçoivent elles-mêmes des insta nces
de classes templates et celà sans avoir à surcharger ces méthodes p our
chaque type possible de la classe template paramètre, soit pouvoir
écrire des définitions :

template<class T>
Foo::bar(std::vector< Element <T> >& vec)

plutôt que :

Foo::bar(std::vector< Element <int> >& vec) { ... }
Foo::bar(std::vector< Element <float> >& vec) { ... }


J'irais dans l'autre sens :

template< typename Container >
Foo::bar( Container& vec ) ...

dont je me rends compte en l'écrivant qu'elles ne résoudraient
d'ailleurs pas mon problème car in-fine je voudrais récupérer 'vec'
dans une variable d'instance 'Foo::_vec' dont je ne vois pas de quel
type elle pourrait être sauf à écrire un truc de genre
std::vector< Element<T> > _vec;
:-/


Si c'est dans la fonction, c'est bien. Si c'est un membre de la
classe, c'est plus complexe. Tu peux le faire en introduisant
une indirection supplémentaire, mais selon les cas, c'est
peut-être plus intéressant de définir des conversions entre les
différents Element, et utiliser un type canonique (disons
Element< double >).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Jean-Marc Bourguet
btzaf writes:

template<class T>
class Foo
{
std::vector< Element<T> > _vec;
public:
void bar (std::vector< Element<T> >& vec) { _vec = vec; }
};


Foo<Element<int> > foo;
Foo<int> n'irait il pas mieux?

foo.bar(vec);
}


A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
btzaf
Supérficiellement, ces phrases ressemblent à l'anglais, mais
c'est un anglais trop complexe pour que je le comprenne (et mon
anglais, en général, n'est pas si mal que ça). Mais j'ai


:-)

La solution la plus simple, peut-être, c'est d'écrire quelque
chose du genre :

template< typename Container >
void
Foo::bar( Container& tableau ) { /* ... */ }


Bon sang mais c'est bien sûr ! On définit en quelque sorte un alias vers
le type.

De tête, je ne vois pas de moyen à contraindre que Container
soit une instance de std::vector. Mais est-ce que c'est
important, du moment qu'il supporte toutes les opérations du
vector qui m'interesse ? D'ailleurs, il se peut bien qu'un tel
test n'est pas nécessaire. Si, par exemple, tu utilises
autrement des opérations sur Container::value_type qui ne sont
valides que sur un Element<T>.


Sans doute pas. Je verrai sur le "vrai" code. En attendant le code test
suivant marche :

8<---
#include <vector>
#include <iostream>

// Element
template<class T>
class Element
{
T _value;
public:
Element(T value) : _value(value) {}
T getValue (void ) const { return _value; }
};


// Foo
template<class Container>
class Foo
{
Container _vec;
public:
void bar (Container& vec) { _vec = vec; }
void display(void);
};

template<class Container>
void Foo<Container>::display(void)
{
std::cout << "Vector size is " << _vec.size() << "n";
}

int main(void)
{
Element<int> eint1(25);
Element<int> eint2(34);
std::vector< Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo<std::vector< Element <int> > > foo;
foo.bar(vec);
foo.display();
}

8<---

Si c'est dans la fonction, c'est bien. Si c'est un membre de la
classe, c'est plus complexe. Tu peux le faire en introduisant
une indirection supplémentaire, mais selon les cas, c'est
peut-être plus intéressant de définir des conversions entre les
différents Element, et utiliser un type canonique (disons
Element< double >).


Non, il s'agit bien de le récupérer dans un membre de la classe. Je
verrai le détail de ce point là en codant la "vraie" classe.

Merci infiniment !!!

Avatar
Michel Decima
On Oct 18, 9:31 am, btzaf wrote:

De fait, ce que je veux c'est permettre à une méthode d'une
autre classe (appelons-là Foo) de recevoir un vecteur
d'Element :

template<class T>
Foo::bar(std::vector< Element <T> >& tableau)

ce qui me permettrait d'avoir des choses comme

Element<int> eint1, eint2;
std::vector<Element <int> > vec;
vec.push_back(eint1);
vec.push_back(eint2);
Foo foo;
foo.bar(vec);

Seulement ce code ne compile pas,


Je crois que c'est une contexte « non-deduced ». J'ai beaucoup
de mal à comprendre l'anglais de la norme ici -- aussi, la
texte dans la proposition la plus récente diffère assez de celui
de la norme 1998 --, mais la norme dit :


Je ne suis pas sur qu'il est necessaire d'aller si loin. Le code
d'origine compile, si on prend en compte la correction de Jean-Marc.


Avatar
btzaf
Je ne suis pas sur qu'il est necessaire d'aller si loin. Le code
d'origine compile, si on prend en compte la correction de Jean-Marc.


Sans aucun doute ! C'est la solution la plus directe (et d'une logique
limpide) à mon problème.

Je garde quand même dans un coin de ma mémoire la solution de James pour
un cas où on aurait besoin d'un conteneur qui ne soit ni n'hérite
directement de std::vector mais l'encapsule par composition.

Merci à tous !