Aurelien Regat-Barrel wrote:Ok. Avec un autre const, ça marche:
char * a = 0;
char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a;
std::vector<char> const & b = a; // OK
std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que
le type qu'on met dans un vector doit supporter
l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement
sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute
personne ne les a pas proposées pour la standardisation parce que
les cas où elles seraient utiles se présentent trop rarement dans
la pratique). Par exemple
std::vector<char*>::iterator ==> std::vector<char
const*>::const_iterator
std::vector<char>::iterator* ==> std::vector<char>::const_iterator
const*
(Note: Ces deux conversions passeront la compilation sur les
implémentations
où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'.
Ces
implémentations semblent toutefois devenir de plus en plus rares ces
jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs
sur les fonctions comme
char* (*)() ===> char const* (*)()
void (*)(char const*) ===> void (*)(char*)
Aurelien Regat-Barrel wrote:
Ok. Avec un autre const, ça marche:
char * a = 0;
char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a;
std::vector<char> const & b = a; // OK
std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que
le type qu'on met dans un vector doit supporter
l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement
sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute
personne ne les a pas proposées pour la standardisation parce que
les cas où elles seraient utiles se présentent trop rarement dans
la pratique). Par exemple
std::vector<char*>::iterator ==> std::vector<char
const*>::const_iterator
std::vector<char>::iterator* ==> std::vector<char>::const_iterator
const*
(Note: Ces deux conversions passeront la compilation sur les
implémentations
où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'.
Ces
implémentations semblent toutefois devenir de plus en plus rares ces
jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs
sur les fonctions comme
char* (*)() ===> char const* (*)()
void (*)(char const*) ===> void (*)(char*)
Aurelien Regat-Barrel wrote:Ok. Avec un autre const, ça marche:
char * a = 0;
char const * const & b = a;
Du coup, je m'attends à pouvoir écrire:
std::vector<char> a;
std::vector<char> const & b = a; // OK
std::vector<char const> const & c = a; // :-(
std::vector<char const> ne passe pas parce que
le type qu'on met dans un vector doit supporter
l'affectation.
En revanche, on peut imaginer des conversions qui seraient parfaitement
sûres mais qui ne sont néanmoins pas supportées par le langage (sans doute
personne ne les a pas proposées pour la standardisation parce que
les cas où elles seraient utiles se présentent trop rarement dans
la pratique). Par exemple
std::vector<char*>::iterator ==> std::vector<char
const*>::const_iterator
std::vector<char>::iterator* ==> std::vector<char>::const_iterator
const*
(Note: Ces deux conversions passeront la compilation sur les
implémentations
où 'std::vector<T>::[[const_]]iterator' est défini comme 'T [[const]]*'.
Ces
implémentations semblent toutefois devenir de plus en plus rares ces
jours-ci.)
Similairement, il pourrait aussi y avoir des conversions entre pointeurs
sur les fonctions comme
char* (*)() ===> char const* (*)()
void (*)(char const*) ===> void (*)(char*)
Aurelien Regat-Barrel wrote:
[...]Après réflexion, je réalise que c'est au niveau des références
qu'est le problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ;
char const* & b = a ;
char const c = 'x' ;
b = &c ;
Et où est-ce que « a » pointe maintenant ?A peu de chose près, j'imagine que c'est la même règle qui
s'applique pour le refus d'initialiser une référence sur un
template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents. Ce
qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être
le cas. C'est facile de trouver des cas particuliers où ça
serait commode si, mais essaie de formuler une règle générale et
absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas
voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque
élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de
chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation
tu t'attends, mais déjà avec la génération d'un temporaire, tu
as (ou aurais) une copie profonde. Cette forme ne serait pas
pire.
Aurelien Regat-Barrel wrote:
[...]
Après réflexion, je réalise que c'est au niveau des références
qu'est le problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ;
char const* & b = a ;
char const c = 'x' ;
b = &c ;
Et où est-ce que « a » pointe maintenant ?
A peu de chose près, j'imagine que c'est la même règle qui
s'applique pour le refus d'initialiser une référence sur un
template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents. Ce
qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être
le cas. C'est facile de trouver des cas particuliers où ça
serait commode si, mais essaie de formuler une règle générale et
absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas
voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque
élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de
chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation
tu t'attends, mais déjà avec la génération d'un temporaire, tu
as (ou aurais) une copie profonde. Cette forme ne serait pas
pire.
Aurelien Regat-Barrel wrote:
[...]Après réflexion, je réalise que c'est au niveau des références
qu'est le problème. Ceci compile:
char a;
char const & b = a;
mais pas ceci:
char * a = 0;
char const * & b = a;
En fait, je ne comprends pas pourquoi.
Parce que :
char * a = 0 ;
char const* & b = a ;
char const c = 'x' ;
b = &c ;
Et où est-ce que « a » pointe maintenant ?A peu de chose près, j'imagine que c'est la même règle qui
s'applique pour le refus d'initialiser une référence sur un
template<T const> à partir d'un template<T>.
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents. Ce
qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas être
le cas. C'est facile de trouver des cas particuliers où ça
serait commode si, mais essaie de formuler une règle générale et
absolue qui s'appliquerait à tous les templates.
Du coup, j'ai une copie profonde que je n'ai peut-être pas
voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur chaque
élément du vecteur.
Je pense que je vais opter pour cette solution, avec un peu de
chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle optimisation
tu t'attends, mais déjà avec la génération d'un temporaire, tu
as (ou aurais) une copie profonde. Cette forme ne serait pas
pire.
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents.
Ce qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas
être le cas. C'est facile de trouver des cas particuliers où
ça serait commode si, mais essaie de formuler une règle
générale et absolue qui s'appliquerait à tous les templates.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
Du coup, j'ai une copie profonde que je n'ai peut-être
pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours
écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur
chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un
peu de chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle
optimisation tu t'attends, mais déjà avec la génération d'un
temporaire, tu as (ou aurais) une copie profonde. Cette
forme ne serait pas pire.
L'optimisation que j'attends, c'est que le compilateur se
rende compte que la construction du AConstVect temporaire est
inutile et qu'il peut directement utiliser v...
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents.
Ce qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas
être le cas. C'est facile de trouver des cas particuliers où
ça serait commode si, mais essaie de formuler une règle
générale et absolue qui s'appliquerait à tous les templates.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
Du coup, j'ai une copie profonde que je n'ai peut-être
pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours
écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur
chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un
peu de chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle
optimisation tu t'attends, mais déjà avec la génération d'un
temporaire, tu as (ou aurais) une copie profonde. Cette
forme ne serait pas pire.
L'optimisation que j'attends, c'est que le compilateur se
rende compte que la construction du AConstVect temporaire est
inutile et qu'il peut directement utiliser v...
Pas tout à fait. T et T const sont deux types distincts.
Apparenté, bien sûr, mais quand même des types différents.
Ce qui fait qu'ils donnent lieu à des instances de template
distinctes. Seulement, il n'y a pas d'apparenté entre les
différentes instances d'un template, même s'il y en a entre
leurs paramètres.
Dans la pratique, je ne vois pas comment ça pourrait ne pas
être le cas. C'est facile de trouver des cas particuliers où
ça serait commode si, mais essaie de formuler une règle
générale et absolue qui s'appliquerait à tous les templates.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
Du coup, j'ai une copie profonde que je n'ai peut-être
pas voulue. Ça coûte cher, copier tout un vecteur.
Note bien que si tu veux la copie, tu peux toujours
écrire :
Test2( AConstVect( v.begin(), v.end() ) ) ;
C'est la façon à appliquer la conversion explicite sur
chaque élément du vecteur.
Je pense que je vais opter pour cette solution, avec un
peu de chance le compilateur fera une bonne optimisation.
Disons que c'est l'inverse. Je ne sais pas à quelle
optimisation tu t'attends, mais déjà avec la génération d'un
temporaire, tu as (ou aurais) une copie profonde. Cette
forme ne serait pas pire.
L'optimisation que j'attends, c'est que le compilateur se
rende compte que la construction du AConstVect temporaire est
inutile et qu'il peut directement utiliser v...
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreurc'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait servir à
la place d'un array<Base>, par exemple ?
Il y a une technique dans la bibliothèque standard qui permet de
faire ce genre de chose : les constructeurs templatés à deux
itérateurs. Dans le cas de la bibliothèque standard, il faut en
être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites dans
tous les sens.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait servir à
la place d'un array<Base>, par exemple ?
Il y a une technique dans la bibliothèque standard qui permet de
faire ce genre de chose : les constructeurs templatés à deux
itérateurs. Dans le cas de la bibliothèque standard, il faut en
être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites dans
tous les sens.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert le
principe d'"array covariance":array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreurc'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait servir à
la place d'un array<Base>, par exemple ?
Il y a une technique dans la bibliothèque standard qui permet de
faire ce genre de chose : les constructeurs templatés à deux
itérateurs. Dans le cas de la bibliothèque standard, il faut en
être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites dans
tous les sens.
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert
le principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert
le principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
J'en suis incapable, mais j'ai tendance à penser que c'est
faisable. Je m'initie doucement à C++/CLI et j'ai découvert
le principe d'"array covariance":
array<int> ^a = gcnew array<int>{ 1, 2, 3 };
array<const int> ^b = a;
a[ 0 ] = 4;
Console::WriteLine( b[ 0 ] ); // affiche 4
b[ 0 ] = 5; // erreur
array<int> ^c = b; // erreur
c'est pas vraiment comparable (array n'est pas un template),
mais je trouve cette possibilité plutôt sympa.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Ça poserait pas mal de problème en C++. Quelle est la taille
d'un élément. (En C#, si je ne me trompe pas, c'est comme en
Java : les éléments sont tous des pointeurs.)
Note aussi que ce n'est pas sans danger ; en Java, au moins,
c'est un des rares cas où on peut violer le système de typage :
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
Je comprends bien -- à condition de le limiter à ces deux
types de conversions, et que pour les pointeurs et les
références dans le dernier cas. En revanche, je ne crois pas que
je saurais la définir.
Ce qui pourrait être utile, c'est une façade pour les
collections standard -- une classe qui présente l'interface
d'une collection, mais renvoie à un autre objet pour les données
réeles. Du coup, toutes les fonctions d'accès (y compris celles
des itérateurs) s'occuperait des conversions. En plus, elles ne
pourraient être instanciées que si la conversion implicite était
légale. Donc, si j'ai une Fassade< Base*, std::vector< Derived*, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
d'insertion, en revanche, non, parce qu'elles essaient
d'effectuer une conversion implicite Base* vers Derived*.
Ce que je suggère, si tu le crois important, c'est de
l'implémenter et de le proposer à Boost. Ensuite, il y a des
chances qu'il fasse partie de C++1x.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Ça poserait pas mal de problème en C++. Quelle est la taille
d'un élément. (En C#, si je ne me trompe pas, c'est comme en
Java : les éléments sont tous des pointeurs.)
Note aussi que ce n'est pas sans danger ; en Java, au moins,
c'est un des rares cas où on peut violer le système de typage :
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
Je comprends bien -- à condition de le limiter à ces deux
types de conversions, et que pour les pointeurs et les
références dans le dernier cas. En revanche, je ne crois pas que
je saurais la définir.
Ce qui pourrait être utile, c'est une façade pour les
collections standard -- une classe qui présente l'interface
d'une collection, mais renvoie à un autre objet pour les données
réeles. Du coup, toutes les fonctions d'accès (y compris celles
des itérateurs) s'occuperait des conversions. En plus, elles ne
pourraient être instanciées que si la conversion implicite était
légale. Donc, si j'ai une Fassade< Base*, std::vector< Derived*
, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
d'insertion, en revanche, non, parce qu'elles essaient
d'effectuer une conversion implicite Base* vers Derived*.
Ce que je suggère, si tu le crois important, c'est de
l'implémenter et de le proposer à Boost. Ensuite, il y a des
chances qu'il fasse partie de C++1x.
C'est une chose d'introduire la covariance dans certaines
structures bien précises ; c'est une autre d'en faire une
principe générale. Est-ce qu'un array<Derived> pourrait
servir à la place d'un array<Base>, par exemple ?
Oui.
http://msdn2.microsoft.com/en-US/library/y8ez8w64(VS.80).aspx
Ça poserait pas mal de problème en C++. Quelle est la taille
d'un élément. (En C#, si je ne me trompe pas, c'est comme en
Java : les éléments sont tous des pointeurs.)
Note aussi que ce n'est pas sans danger ; en Java, au moins,
c'est un des rares cas où on peut violer le système de typage :
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Il y a une technique dans la bibliothèque standard qui permet
de faire ce genre de chose : les constructeurs templatés à
deux itérateurs. Dans le cas de la bibliothèque standard, il
faut en être explicite, mais je ne suis pas sûr que c'est un
désavantage ; je n'aime pas trop des conversions implicites
dans tous les sens.
Pour moi, il s'agit de généraliser des conversions implicites
déjà existantes (non const -> const, derived -> base, ...).
Je comprends bien -- à condition de le limiter à ces deux
types de conversions, et que pour les pointeurs et les
références dans le dernier cas. En revanche, je ne crois pas que
je saurais la définir.
Ce qui pourrait être utile, c'est une façade pour les
collections standard -- une classe qui présente l'interface
d'une collection, mais renvoie à un autre objet pour les données
réeles. Du coup, toutes les fonctions d'accès (y compris celles
des itérateurs) s'occuperait des conversions. En plus, elles ne
pourraient être instanciées que si la conversion implicite était
légale. Donc, si j'ai une Fassade< Base*, std::vector< Derived*, toutes les fonctions de lecture marchent, parce qu'elles
effectue une conversion Derived* en Base* ; les fonctions
d'insertion, en revanche, non, parce qu'elles essaient
d'effectuer une conversion implicite Base* vers Derived*.
Ce que je suggère, si tu le crois important, c'est de
l'implémenter et de le proposer à Boost. Ensuite, il y a des
chances qu'il fasse partie de C++1x.
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
void f( Base[] b )
{
b[ 0 ] = new Base() ;
}
// ...
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
// Et le type de d[0] est...
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
void f( Base[] b ){
b[ 0 ] = new Base() ;
}
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
Java aussi, je crois. Mais le fait reste que tu as suivi les
règles, et que tu as violé le système du typage quand même.
void f( Base[] b ){
b[ 0 ] = new Base() ;
}
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
Java aussi, je crois. Mais le fait reste que tu as suivi les
règles, et que tu as violé le système du typage quand même.
void f( Base[] b ){
b[ 0 ] = new Base() ;
}
Derived d[] = new Derived[ 10 ] ;
f( d ) ;
Pour info, en C++/CLI, cet exemple (adapté) lève une exception
ArrayTypeMismatchException.
Java aussi, je crois. Mais le fait reste que tu as suivi les
règles, et que tu as violé le système du typage quand même.