OVH Cloud OVH Cloud

Classes imbriquees, deductabilite, iterateur

3 réponses
Avatar
Jean-Marc Bourguet
Dans un vieux message, Gabriel me repondait:

> > > C'est une vieille disussion : si conteneur<type>::iterator est
> > > réellement une classe imbriquée alors souvent type sera dans un
> > > contexte non déductible. James en avait fait l'expérience, je crois.
> >
> > C'est des exemples de contextes que je voudrais.
>
> Cela arrive quand les gens veulent écrire des fonctions templates,
> en exprimant la contrainte que la classe est vraiment imbriquée dans
> une certaine classe donnée, par exemple ils veulent que leurs
> fonctions opèrent sur un vector<T>::iterator et pas sur un
> list<T>::iterator. Alors, typiquement ils vont exprimer la contrainte
> comme cela ; évidemment cela ne marche pas.
>
> template<class T>
> struct vector {
> struct iterator { /* ... */ };
> // ...
> };
>
> template<class T>
> struct list {
> struct iterator { /* ... */ };
> // ...
> };
>
> template<class T>
> void sort(typename vector<T>::iterator f, typename vector<T>::iterator l);
>
> Ou, cela peut être aussi des contraintes sur le T, i.e. distinguer un
> T* d'un U...
>
> En général, j'utilise quand même les classes imbriquées dans les
> templates. J'exprime les contraintes différemment -- et cela demande
> un peu plus de travail mais on peut vivrte avec.

Il me semble que le probleme doit exister aussi si iterator n'est pas
une classe imbriquee mais un typedef vers une classe externe. En tout
cas les compilateurs que j'ai essaie (como online, g++ 4.0.2, Sun C++
5.7 Patch 117830-03 2005/07/21) ont tous le meme comportement sur le
code:

template <typename T>
class Container1 {
public:
class iterator {
};
};

template <typename T>
class Container2Iterator {
};

template <typename T>
class Container2 {
public:
typedef Container2Iterator<T> iterator;
};

template <typename T>
void f1(typename Container1<T>::iterator) {
}

template <typename T>
void f2(typename Container2<T>::iterator) {
}

template <typename T>
void f2bis(Container2Iterator<T>) {
}

void f() {
Container1<int>::iterator i1;
Container2<int>::iterator i2;

f1(i1);
f2(i2);
f2bis(i2);
}

f1 et f2 ne passent pas et f2bis passe.

Sachant qu'on veut utiliser l'interface C<T>::iterator, y a-t'il un
avantage intrinseque quelconque a utiliser l'une forme ou l'autre ?

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

3 réponses

Avatar
Gabriel Dos Reis
Jean-Marc Bourguet writes:

| Dans un vieux message, Gabriel me repondait:
|
| > > > C'est une vieille disussion : si conteneur<type>::iterator est
| > > > réellement une classe imbriquée alors souvent type sera dans un
| > > > contexte non déductible. James en avait fait l'expérience, je crois.
| > >
| > > C'est des exemples de contextes que je voudrais.
| >
| > Cela arrive quand les gens veulent écrire des fonctions templates,
| > en exprimant la contrainte que la classe est vraiment imbriquée dans
| > une certaine classe donnée, par exemple ils veulent que leurs
| > fonctions opèrent sur un vector<T>::iterator et pas sur un
| > list<T>::iterator. Alors, typiquement ils vont exprimer la contrainte
| > comme cela ; évidemment cela ne marche pas.
| >
| > template<class T>
| > struct vector {
| > struct iterator { /* ... */ };
| > // ...
| > };
| >
| > template<class T>
| > struct list {
| > struct iterator { /* ... */ };
| > // ...
| > };
| >
| > template<class T>
| > void sort(typename vector<T>::iterator f, typename vector<T>::iterator l);
| >
| > Ou, cela peut être aussi des contraintes sur le T, i.e. distinguer un
| > T* d'un U...
| >
| > En général, j'utilise quand même les classes imbriquées dans les
| > templates. J'exprime les contraintes différemment -- et cela demande
| > un peu plus de travail mais on peut vivrte avec.
|
| Il me semble que le probleme doit exister aussi si iterator n'est pas
| une classe imbriquee mais un typedef vers une classe externe.

Oui, tu as raison. La notion exacte est « nested types » et pas juste
class imbriquée -- car elle englobe bien les classes imbriquees et les
typedefs définis à l'intérieur d'une classe.

| En tout
| cas les compilateurs que j'ai essaie (como online, g++ 4.0.2, Sun C++
| 5.7 Patch 117830-03 2005/07/21) ont tous le meme comportement sur le
| code:
|
| template <typename T>
| class Container1 {
| public:
| class iterator {
| };
| };
|
| template <typename T>
| class Container2Iterator {
| };
|
| template <typename T>
| class Container2 {
| public:
| typedef Container2Iterator<T> iterator;
| };
|
| template <typename T>
| void f1(typename Container1<T>::iterator) {
| }
|
| template <typename T>
| void f2(typename Container2<T>::iterator) {
| }
|
| template <typename T>
| void f2bis(Container2Iterator<T>) {
| }
|
| void f() {
| Container1<int>::iterator i1;
| Container2<int>::iterator i2;
|
| f1(i1);
| f2(i2);
| f2bis(i2);
| }
|
| f1 et f2 ne passent pas et f2bis passe.
|
| Sachant qu'on veut utiliser l'interface C<T>::iterator, y a-t'il un
| avantage intrinseque quelconque a utiliser l'une forme ou l'autre ?

si tu as des garanties que C<T>::iterator sera toujours
Container2<T>::iterator, alors va pour f2bis. Mais si tu veux
enforcer cette contrainte, je ne vois que deux solutions pour le
moment :

(1) SFINAE hackery ;
(2) parametrer l'itérateur par le container (un peu comme nous
l'avons fait pour std::vector<T>::iterator pour le distinguer
d'un pointeur brut).

template<class Container>
struct Iterator { ... };

ou

template<template<class> class Cont, class ValueType>
struct Iterator { ... };

-- Gaby
Avatar
Jean-Marc Bourguet
Gabriel Dos Reis writes:

Jean-Marc Bourguet writes:

| Dans un vieux message, Gabriel me repondait:
|
| > > > C'est une vieille disussion : si conteneur<type>::iterator est
| > > > réellement une classe imbriquée alors souvent type sera dans un
| > > > contexte non déductible. James en avait fait l'expérience, je crois.
| > >
| > > C'est des exemples de contextes que je voudrais.
| >
| > Cela arrive quand les gens veulent écrire des fonctions templates,
| > en exprimant la contrainte que la classe est vraiment imbriquée dans
| > une certaine classe donnée, par exemple ils veulent que leurs
| > fonctions opèrent sur un vector<T>::iterator et pas sur un
| > list<T>::iterator. Alors, typiquement ils vont exprimer la contrainte
| > comme cela ; évidemment cela ne marche pas.
| >
| > template<class T>
| > struct vector {
| > struct iterator { /* ... */ };
| > // ...
| > };
| >
| > template<class T>
| > struct list {
| > struct iterator { /* ... */ };
| > // ...
| > };
| >
| > template<class T>
| > void sort(typename vector<T>::iterator f, typename vector<T>::iterator l);
| >
| > Ou, cela peut être aussi des contraintes sur le T, i.e. distinguer un
| > T* d'un U...
| >
| > En général, j'utilise quand même les classes imbriquées dans les
| > templates. J'exprime les contraintes différemment -- et cela demande
| > un peu plus de travail mais on peut vivrte avec.
|
| Il me semble que le probleme doit exister aussi si iterator n'est pas
| une classe imbriquee mais un typedef vers une classe externe.

Oui, tu as raison. La notion exacte est « nested types » et pas juste
class imbriquée -- car elle englobe bien les classes imbriquees et les
typedefs définis à l'intérieur d'une classe.

| En tout
| cas les compilateurs que j'ai essaie (como online, g++ 4.0.2, Sun C++
| 5.7 Patch 117830-03 2005/07/21) ont tous le meme comportement sur le
| code:
|
| template <typename T>
| class Container1 {
| public:
| class iterator {
| };
| };
|
| template <typename T>
| class Container2Iterator {
| };
|
| template <typename T>
| class Container2 {
| public:
| typedef Container2Iterator<T> iterator;
| };
|
| template <typename T>
| void f1(typename Container1<T>::iterator) {
| }
|
| template <typename T>
| void f2(typename Container2<T>::iterator) {
| }
|
| template <typename T>
| void f2bis(Container2Iterator<T>) {
| }
|
| void f() {
| Container1<int>::iterator i1;
| Container2<int>::iterator i2;
|
| f1(i1);
| f2(i2);
| f2bis(i2);
| }
|
| f1 et f2 ne passent pas et f2bis passe.
|
| Sachant qu'on veut utiliser l'interface C<T>::iterator, y a-t'il un
| avantage intrinseque quelconque a utiliser l'une forme ou l'autre ?

si tu as des garanties que C<T>::iterator sera toujours
Container2<T>::iterator, alors va pour f2bis.


Je me suis mal exprime. J'etais du point de vue de celui qui ecrit
Container. Est-ce qu'utiliser la methode de Container1 ou celle de
Container2 apporte quelque chose aux utilisateurs? J'avais tendance a
utiliser celle de Container2 parce qu'avais conclu que oui par erreur
suite a ce thread. Maintenant il me semble que pour l'utilisateur le
choix est indifferent.

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
Gabriel Dos Reis
Jean-Marc Bourguet writes:

[...]

| > | En tout
| > | cas les compilateurs que j'ai essaie (como online, g++ 4.0.2, Sun C++
| > | 5.7 Patch 117830-03 2005/07/21) ont tous le meme comportement sur le
| > | code:
| > |
| > | template <typename T>
| > | class Container1 {
| > | public:
| > | class iterator {
| > | };
| > | };
| > |
| > | template <typename T>
| > | class Container2Iterator {
| > | };
| > |
| > | template <typename T>
| > | class Container2 {
| > | public:
| > | typedef Container2Iterator<T> iterator;
| > | };
| > |
| > | template <typename T>
| > | void f1(typename Container1<T>::iterator) {
| > | }
| > |
| > | template <typename T>
| > | void f2(typename Container2<T>::iterator) {
| > | }
| > |
| > | template <typename T>
| > | void f2bis(Container2Iterator<T>) {
| > | }
| > |
| > | void f() {
| > | Container1<int>::iterator i1;
| > | Container2<int>::iterator i2;
| > |
| > | f1(i1);
| > | f2(i2);
| > | f2bis(i2);
| > | }
| > |
| > | f1 et f2 ne passent pas et f2bis passe.
| > |
| > | Sachant qu'on veut utiliser l'interface C<T>::iterator, y a-t'il un
| > | avantage intrinseque quelconque a utiliser l'une forme ou l'autre ?
| >
| > si tu as des garanties que C<T>::iterator sera toujours
| > Container2<T>::iterator, alors va pour f2bis.
|
| Je me suis mal exprime. J'etais du point de vue de celui qui ecrit
| Container. Est-ce qu'utiliser la methode de Container1 ou celle de
| Container2 apporte quelque chose aux utilisateurs?

aucun bénéfice qui me saute aux yeux.

| J'avais tendance a
| utiliser celle de Container2 parce qu'avais conclu que oui par erreur
| suite a ce thread. Maintenant il me semble que pour l'utilisateur le
| choix est indifferent.

désolé d'avoir causé la confusion par l'utilisation de termes trop
restrictifs.

On peut arguer que la méthode Container1 encapsule « plus », mais je
ne sais pas si c'est un argument décisif.

-- Gaby