Je dispose d'une classe de /smart pointer/ et je cherche à implanter
l'opérateur [] dessus, et déléguer l'appel à l'objet encapsulé pour
des raisons de confort d'utilisation :
template <typename T>
class ref // très simplifié...
{
public:
même si ça fait un peu "bidouille", le code compile et fonctionne
correctement.
Seulement maintenant, j'ai un autre problème. Il faudrait également que
je puisse faire quelque chose comme :
std::cout << x[2.0];
et là, j'obtiens tout un tas d'erreur concernant un "ambiguous
overload", ce qui est normal car ostream::operator<<() accepte
tout un tas de types.
Pourtant... il n'y a qu'une seule version de obj::operator[] et pas de
conversion implicite possible (a priori). Pourquoi le compilateur
ne parvient-il pas à résoudre l'ambiguïté ?
Comment résoudre ce problème ?
Et déjà, est-ce faisable au moins ? :-)
En effet ici, le compilateur n'a aucun moyen de résoudre Y. Je ne connais pas trop les détails du pouquoi du comment, mais il y a certainement ici quelqu'un pour t'éclairer.
Seulement maintenant, j'ai un autre problème. Il faudrait également que je puisse faire quelque chose comme :
std::cout << x[2.0];
et là, j'obtiens tout un tas d'erreur concernant un "ambiguous overload", ce qui est normal car ostream::operator<<() accepte tout un tas de types.
Pourtant... il n'y a qu'une seule version de obj::operator[] et pas de conversion implicite possible (a priori). Pourquoi le compilateur ne parvient-il pas à résoudre l'ambiguïté ?
Comment veux-tu qu'il sache quel est le bon type qu'il doit utiliser pour instancier l'opérateur de conversion du proxy? Quand tu l'affecte à un int, ok. Mais sinon?
Comment résoudre ce problème ? Et déjà, est-ce faisable au moins ? :-)
Si tu peux modifier ta classe obj, tu peux y ajouter:
struct obj { typedef int return_type; // .... };
Et alors l'opérateur [] dans ref s'écrit ainsi: template<typename X> X::return_type operator[](const X& p) const { return (*m_ptr)[p]; }
Si tu ne peux pas modifier obj, tu utilises une méta-fonction:
template<typename T> struct my_return_type { };
template<> struct my_return_type<obj> { typedef int type; };
et alors dans la classe ref, l'opérateur [] s'écrit: template<typename X> my_return_type<X>::type operator[](const X& p) const { return (*m_ptr)[p]; }
-- "I have nothing to declare except war on Austria." ~ Oscar Wilde, 1936
En effet ici, le compilateur n'a aucun moyen de résoudre Y. Je ne
connais pas trop les détails du pouquoi du comment, mais il y a
certainement ici quelqu'un pour t'éclairer.
Seulement maintenant, j'ai un autre problème. Il faudrait également que
je puisse faire quelque chose comme :
std::cout << x[2.0];
et là, j'obtiens tout un tas d'erreur concernant un "ambiguous
overload", ce qui est normal car ostream::operator<<() accepte
tout un tas de types.
Pourtant... il n'y a qu'une seule version de obj::operator[] et pas de
conversion implicite possible (a priori). Pourquoi le compilateur
ne parvient-il pas à résoudre l'ambiguïté ?
Comment veux-tu qu'il sache quel est le bon type qu'il doit utiliser
pour instancier l'opérateur de conversion du proxy? Quand tu l'affecte à
un int, ok. Mais sinon?
Comment résoudre ce problème ?
Et déjà, est-ce faisable au moins ? :-)
Si tu peux modifier ta classe obj, tu peux y ajouter:
struct obj
{
typedef int return_type;
// ....
};
Et alors l'opérateur [] dans ref s'écrit ainsi:
template<typename X>
X::return_type operator[](const X& p) const
{
return (*m_ptr)[p];
}
Si tu ne peux pas modifier obj, tu utilises une méta-fonction:
template<typename T>
struct my_return_type
{
};
template<>
struct my_return_type<obj>
{
typedef int type;
};
et alors dans la classe ref, l'opérateur [] s'écrit:
template<typename X>
my_return_type<X>::type operator[](const X& p) const
{
return (*m_ptr)[p];
}
--
"I have nothing to declare except war on Austria." ~ Oscar Wilde, 1936
En effet ici, le compilateur n'a aucun moyen de résoudre Y. Je ne connais pas trop les détails du pouquoi du comment, mais il y a certainement ici quelqu'un pour t'éclairer.
Seulement maintenant, j'ai un autre problème. Il faudrait également que je puisse faire quelque chose comme :
std::cout << x[2.0];
et là, j'obtiens tout un tas d'erreur concernant un "ambiguous overload", ce qui est normal car ostream::operator<<() accepte tout un tas de types.
Pourtant... il n'y a qu'une seule version de obj::operator[] et pas de conversion implicite possible (a priori). Pourquoi le compilateur ne parvient-il pas à résoudre l'ambiguïté ?
Comment veux-tu qu'il sache quel est le bon type qu'il doit utiliser pour instancier l'opérateur de conversion du proxy? Quand tu l'affecte à un int, ok. Mais sinon?
Comment résoudre ce problème ? Et déjà, est-ce faisable au moins ? :-)
Si tu peux modifier ta classe obj, tu peux y ajouter:
struct obj { typedef int return_type; // .... };
Et alors l'opérateur [] dans ref s'écrit ainsi: template<typename X> X::return_type operator[](const X& p) const { return (*m_ptr)[p]; }
Si tu ne peux pas modifier obj, tu utilises une méta-fonction:
template<typename T> struct my_return_type { };
template<> struct my_return_type<obj> { typedef int type; };
et alors dans la classe ref, l'opérateur [] s'écrit: template<typename X> my_return_type<X>::type operator[](const X& p) const { return (*m_ptr)[p]; }
-- "I have nothing to declare except war on Austria." ~ Oscar Wilde, 1936
Ca m'apprendra à ne pas tester les morceaux de code que je poste ici :)
-- "I have nothing to declare except war on Austria." ~ Oscar Wilde, 1936
kanze
Vincent Richard wrote:
Je dispose d'une classe de /smart pointer/ et je cherche à implanter l'opérateur [] dessus, et déléguer l'appel à l'objet encapsulé pour des raisons de confort d'utilisation :
template <typename T> class ref // très simplifié... { public:
template <typename X, typename Y> Y operator[](const X& p) const
Qu'est-ce que ça doit signifier ? Si j'applique [] sur un pointeur, la sémantique en est bien définie. Si j'ai un « smart pointeur », si j'implémente operator[], il faut lui donner la même sémantique, faute de semer la confusion chez l'utilisateur. Ici, ça voudrait dire :
T& operator[]( size_t index ) const ;
(Et évidemment, comme pour les vrais pointeurs, ça n'a un sens que si l'objet pointé est en fait un tableau.)
{ return (*m_ptr)[p]; }
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le pointeur, en somme. Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(), executeDailyJob()...
Pour commencer, si tu fais ça, ce n'est plus un smart pointeur, mais un handle ou une façade. Et tu n'implémentes pas operator* et operator-> -- un objet ne doit pas être à la fois un pointeur vers l'objet, et l'objet même. Ensuite, si c'est effectivement un modèle de conception assez utilisé, comme montre ma liste ci-dessus, il faut bien une certaine connaissance de l'objet derrière pour le faire ; l'interface de la façade est celle de l'objet, et il n'y a pas de moyen (à ma connaissance) de le faire génériquement.
-- James Kanze GABI Software 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
Vincent Richard wrote:
Je dispose d'une classe de /smart pointer/ et je cherche à
implanter l'opérateur [] dessus, et déléguer l'appel à l'objet
encapsulé pour des raisons de confort d'utilisation :
template <typename T>
class ref // très simplifié...
{
public:
template <typename X, typename Y>
Y operator[](const X& p) const
Qu'est-ce que ça doit signifier ? Si j'applique [] sur un
pointeur, la sémantique en est bien définie. Si j'ai un « smart
pointeur », si j'implémente operator[], il faut lui donner la
même sémantique, faute de semer la confusion chez l'utilisateur.
Ici, ça voudrait dire :
T& operator[]( size_t index ) const ;
(Et évidemment, comme pour les vrais pointeurs, ça n'a un sens
que si l'objet pointé est en fait un tableau.)
{
return (*m_ptr)[p];
}
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le
pointeur, en somme. Pourquoi pas alors aussi ++, --, +-, -=,
getActivationState(), executeDailyJob()...
Pour commencer, si tu fais ça, ce n'est plus un smart pointeur,
mais un handle ou une façade. Et tu n'implémentes pas operator*
et operator-> -- un objet ne doit pas être à la fois un pointeur
vers l'objet, et l'objet même. Ensuite, si c'est effectivement
un modèle de conception assez utilisé, comme montre ma liste
ci-dessus, il faut bien une certaine connaissance de l'objet
derrière pour le faire ; l'interface de la façade est celle de
l'objet, et il n'y a pas de moyen (à ma connaissance) de le
faire génériquement.
--
James Kanze GABI Software
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
Je dispose d'une classe de /smart pointer/ et je cherche à implanter l'opérateur [] dessus, et déléguer l'appel à l'objet encapsulé pour des raisons de confort d'utilisation :
template <typename T> class ref // très simplifié... { public:
template <typename X, typename Y> Y operator[](const X& p) const
Qu'est-ce que ça doit signifier ? Si j'applique [] sur un pointeur, la sémantique en est bien définie. Si j'ai un « smart pointeur », si j'implémente operator[], il faut lui donner la même sémantique, faute de semer la confusion chez l'utilisateur. Ici, ça voudrait dire :
T& operator[]( size_t index ) const ;
(Et évidemment, comme pour les vrais pointeurs, ça n'a un sens que si l'objet pointé est en fait un tableau.)
{ return (*m_ptr)[p]; }
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le pointeur, en somme. Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(), executeDailyJob()...
Pour commencer, si tu fais ça, ce n'est plus un smart pointeur, mais un handle ou une façade. Et tu n'implémentes pas operator* et operator-> -- un objet ne doit pas être à la fois un pointeur vers l'objet, et l'objet même. Ensuite, si c'est effectivement un modèle de conception assez utilisé, comme montre ma liste ci-dessus, il faut bien une certaine connaissance de l'objet derrière pour le faire ; l'interface de la façade est celle de l'objet, et il n'y a pas de moyen (à ma connaissance) de le faire génériquement.
-- James Kanze GABI Software 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
Vincent Richard
wrote:
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le pointeur, en somme.
C'est bien ce que je cherche à faire.
Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(), executeDailyJob()...
Parce que j'ai seulement besoin de l'opérateur [] ! :-) Je ne voudrais pas casser du code existant qui repose sur cet opérateur.
J'ai une classe 'Part' qui permet de naviguer dans toute une hiérarchie de sous-parties en utilisant l'opérateur [] en séquence, par exemple :
Pour l'instant, les objets renvoyés sont des références (Part&) mais j'aimerais passer ça en smart pointer (pour pouvoir conserver des références fortes dessus), sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un raccourci d'écriture en somme. Mais a priori, ce n'est pas faisable, donc je laisse tomber...
Merci.
Vincent
kanze@gabi-soft.fr wrote:
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le
pointeur, en somme.
C'est bien ce que je cherche à faire.
Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(),
executeDailyJob()...
Parce que j'ai seulement besoin de l'opérateur [] ! :-) Je ne
voudrais pas casser du code existant qui repose sur cet opérateur.
J'ai une classe 'Part' qui permet de naviguer dans toute une
hiérarchie de sous-parties en utilisant l'opérateur [] en séquence,
par exemple :
Pour l'instant, les objets renvoyés sont des références (Part&)
mais j'aimerais passer ça en smart pointer (pour pouvoir conserver
des références fortes dessus), sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un
raccourci d'écriture en somme. Mais a priori, ce n'est pas faisable,
donc je laisse tomber...
Pour l'instant, les objets renvoyés sont des références (Part&) mais j'aimerais passer ça en smart pointer (pour pouvoir conserver des références fortes dessus), sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un raccourci d'écriture en somme. Mais a priori, ce n'est pas faisable, donc je laisse tomber...
Merci.
Vincent
kanze
Vincent Richard wrote:
wrote:
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le pointeur, en somme.
C'est bien ce que je cherche à faire.
Ça me semble d'une mauvaise conception. Est-ce que ta classe est un pointeur, ou une collection ? Il faut savoir. Sinon, ni les utilisateurs, ni ceux qui ont à le maintenir n'y comprendront rien.
Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(), > executeDailyJob()...
Parce que j'ai seulement besoin de l'opérateur [] ! :-)
Pour l'instant.
Je ne voudrais pas casser du code existant qui repose sur cet opérateur.
Alors là, je ne comprends pas. Est-ce que le code existant utilise des pointeurs à un seul objet, ou non ?
-- Si le code existant utilise des collections (avec operator[]), il faut leur fournir une collection, non un pointeur.
-- Si le code existant utilise des pointeurs à des tableaux, ton opérateur[] doit renvoyer le même type que ton operator*, et il n'y a pas de probleème.
-- Si le code existant utilise des pointeurs à des objets simples, il n'utilise pas [] avec eux. Donc, pas de problème non plus.
-- Si le code existant utilise déjà une classe qui est mal conçue, et ton seule but y est de continuer à supporter l'ancienne utilisation, afin de ne pas avoir à rétoucher le code ancien, tu n'as prèsque sûrement pas besoin des templates.
J'ai une classe 'Part' qui permet de naviguer dans toute une hiérarchie de sous-parties en utilisant l'opérateur [] en séquence, par exemple :
Pour l'instant, les objets renvoyés sont des références (Part&) mais j'aimerais passer ça en smart pointer (pour pouvoir conserver des références fortes dessus),
Je ne comprends pas. Qu'est-ce que tu entends par « références fortes » ? Je ne vois pas l'intérêt à utiliser un pointeur intelligent ici.
Je ne sais pas pourquoi les références ne te font pas l'affaire, mais sinon, je verrais plutôt un envéloppe (dans l'idiome lettre/envéloppe). Et de la dérivation et des classes de base, plutôt que des templates.
sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un raccourci d'écriture en somme. Mais a priori, ce n'est pas faisable, donc je laisse tomber...
Il y a, en fait, peut-être une faiblesse du langage. Strictement parlant, ce que tu veux, c'est une référence intelligente (et non un pointeur intelligent). Ce qui est malheureusement impossible, parce qu'on ne peut pas surcharger l'opérateur `.'.
Mais que même avec une référence intelligente, tu te trouves avec le problème qu'il faut surcharger aussi les autres opérateurs qui peuvent s'appliquer à une référence, c-à-d tout opérateur que la classe à laquelle on fait référence a surchargé. Et prèsque par définition, il n'y a pas de solution générique, puisqu'il y a une infinité d'opérateurs possibles. (Pense à () sans paramètres, () avec un paramètre, () avec deux paramètres...)
Ci dessus, en revanche, je crois que tu as donné la réponse : tu n'as besoin que de l'opérateur []. C'est que tu ne recherches pas une solution totalement générique, mais une solution à ton problème exact. À partir de ce moment, je me démande si tu as besoin de tant de templates. Un paramètre de template qui ne serait jamais instancié que sur int ne me paraît peu intéressant.
N'empêche que d'après le peu que tu as écrit, je verrais plutôt une hièrarchie polymorphique, avec l'opérateur [] dans la classe de base, qui renvoie une référence à la classe de base. À la limite, l'opérateur[] pourrait être virtuel, avec un type de retour covariant, mais je craindrais que ça apportera plus de restrictions que de protection.
-- James Kanze GABI Software 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
Vincent Richard wrote:
kanze@gabi-soft.fr wrote:
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le
pointeur, en somme.
C'est bien ce que je cherche à faire.
Ça me semble d'une mauvaise conception. Est-ce que ta classe est
un pointeur, ou une collection ? Il faut savoir. Sinon, ni les
utilisateurs, ni ceux qui ont à le maintenir n'y comprendront
rien.
Pourquoi pas alors aussi ++, --, +-, -=,
getActivationState(), > executeDailyJob()...
Parce que j'ai seulement besoin de l'opérateur [] ! :-)
Pour l'instant.
Je ne voudrais pas casser du code existant qui repose sur cet
opérateur.
Alors là, je ne comprends pas. Est-ce que le code existant
utilise des pointeurs à un seul objet, ou non ?
-- Si le code existant utilise des collections (avec
operator[]), il faut leur fournir une collection, non un
pointeur.
-- Si le code existant utilise des pointeurs à des tableaux,
ton opérateur[] doit renvoyer le même type que ton
operator*, et il n'y a pas de probleème.
-- Si le code existant utilise des pointeurs à des objets
simples, il n'utilise pas [] avec eux. Donc, pas de problème
non plus.
-- Si le code existant utilise déjà une classe qui est mal
conçue, et ton seule but y est de continuer à supporter
l'ancienne utilisation, afin de ne pas avoir à rétoucher le
code ancien, tu n'as prèsque sûrement pas besoin des
templates.
J'ai une classe 'Part' qui permet de naviguer dans toute une
hiérarchie de sous-parties en utilisant l'opérateur [] en
séquence, par exemple :
Pour l'instant, les objets renvoyés sont des références
(Part&) mais j'aimerais passer ça en smart pointer (pour
pouvoir conserver des références fortes dessus),
Je ne comprends pas. Qu'est-ce que tu entends par « références
fortes » ? Je ne vois pas l'intérêt à utiliser un pointeur
intelligent ici.
Je ne sais pas pourquoi les références ne te font pas l'affaire,
mais sinon, je verrais plutôt un envéloppe (dans l'idiome
lettre/envéloppe). Et de la dérivation et des classes de base,
plutôt que des templates.
sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un
raccourci d'écriture en somme. Mais a priori, ce n'est pas
faisable, donc je laisse tomber...
Il y a, en fait, peut-être une faiblesse du langage. Strictement
parlant, ce que tu veux, c'est une référence intelligente (et
non un pointeur intelligent). Ce qui est malheureusement
impossible, parce qu'on ne peut pas surcharger l'opérateur `.'.
Mais que même avec une référence intelligente, tu te trouves
avec le problème qu'il faut surcharger aussi les autres
opérateurs qui peuvent s'appliquer à une référence, c-à-d tout
opérateur que la classe à laquelle on fait référence a
surchargé. Et prèsque par définition, il n'y a pas de solution
générique, puisqu'il y a une infinité d'opérateurs
possibles. (Pense à () sans paramètres, () avec un paramètre, ()
avec deux paramètres...)
Ci dessus, en revanche, je crois que tu as donné la réponse : tu
n'as besoin que de l'opérateur []. C'est que tu ne recherches
pas une solution totalement générique, mais une solution à ton
problème exact. À partir de ce moment, je me démande si tu as
besoin de tant de templates. Un paramètre de template qui ne
serait jamais instancié que sur int ne me paraît peu
intéressant.
N'empêche que d'après le peu que tu as écrit, je verrais plutôt
une hièrarchie polymorphique, avec l'opérateur [] dans la classe
de base, qui renvoie une référence à la classe de base. À la
limite, l'opérateur[] pourrait être virtuel, avec un type de
retour covariant, mais je craindrais que ça apportera plus de
restrictions que de protection.
--
James Kanze GABI Software
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
Tu veux pouvoir utiliser l'objet pointé sans déréférencer le pointeur, en somme.
C'est bien ce que je cherche à faire.
Ça me semble d'une mauvaise conception. Est-ce que ta classe est un pointeur, ou une collection ? Il faut savoir. Sinon, ni les utilisateurs, ni ceux qui ont à le maintenir n'y comprendront rien.
Pourquoi pas alors aussi ++, --, +-, -=, getActivationState(), > executeDailyJob()...
Parce que j'ai seulement besoin de l'opérateur [] ! :-)
Pour l'instant.
Je ne voudrais pas casser du code existant qui repose sur cet opérateur.
Alors là, je ne comprends pas. Est-ce que le code existant utilise des pointeurs à un seul objet, ou non ?
-- Si le code existant utilise des collections (avec operator[]), il faut leur fournir une collection, non un pointeur.
-- Si le code existant utilise des pointeurs à des tableaux, ton opérateur[] doit renvoyer le même type que ton operator*, et il n'y a pas de probleème.
-- Si le code existant utilise des pointeurs à des objets simples, il n'utilise pas [] avec eux. Donc, pas de problème non plus.
-- Si le code existant utilise déjà une classe qui est mal conçue, et ton seule but y est de continuer à supporter l'ancienne utilisation, afin de ne pas avoir à rétoucher le code ancien, tu n'as prèsque sûrement pas besoin des templates.
J'ai une classe 'Part' qui permet de naviguer dans toute une hiérarchie de sous-parties en utilisant l'opérateur [] en séquence, par exemple :
Pour l'instant, les objets renvoyés sont des références (Part&) mais j'aimerais passer ça en smart pointer (pour pouvoir conserver des références fortes dessus),
Je ne comprends pas. Qu'est-ce que tu entends par « références fortes » ? Je ne vois pas l'intérêt à utiliser un pointeur intelligent ici.
Je ne sais pas pourquoi les références ne te font pas l'affaire, mais sinon, je verrais plutôt un envéloppe (dans l'idiome lettre/envéloppe). Et de la dérivation et des classes de base, plutôt que des templates.
sans être obligé de faire :
(*(*(*(*myRootPart)[0])[2])[5])[3]
Je ne vois pas en quoi ça change la sémantique. C'est juste un raccourci d'écriture en somme. Mais a priori, ce n'est pas faisable, donc je laisse tomber...
Il y a, en fait, peut-être une faiblesse du langage. Strictement parlant, ce que tu veux, c'est une référence intelligente (et non un pointeur intelligent). Ce qui est malheureusement impossible, parce qu'on ne peut pas surcharger l'opérateur `.'.
Mais que même avec une référence intelligente, tu te trouves avec le problème qu'il faut surcharger aussi les autres opérateurs qui peuvent s'appliquer à une référence, c-à-d tout opérateur que la classe à laquelle on fait référence a surchargé. Et prèsque par définition, il n'y a pas de solution générique, puisqu'il y a une infinité d'opérateurs possibles. (Pense à () sans paramètres, () avec un paramètre, () avec deux paramètres...)
Ci dessus, en revanche, je crois que tu as donné la réponse : tu n'as besoin que de l'opérateur []. C'est que tu ne recherches pas une solution totalement générique, mais une solution à ton problème exact. À partir de ce moment, je me démande si tu as besoin de tant de templates. Un paramètre de template qui ne serait jamais instancié que sur int ne me paraît peu intéressant.
N'empêche que d'après le peu que tu as écrit, je verrais plutôt une hièrarchie polymorphique, avec l'opérateur [] dans la classe de base, qui renvoie une référence à la classe de base. À la limite, l'opérateur[] pourrait être virtuel, avec un type de retour covariant, mais je craindrais que ça apportera plus de restrictions que de protection.
-- James Kanze GABI Software 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