OVH Cloud OVH Cloud

Sens de const

64 réponses
Avatar
Vincent Lascaux
Bonjour,

Je voudrais savoir quel sens vous mettez derriere le mot clé "const", ou
plutot quel contrat une fonction qui a des parametres const doit remplir.

Pour être plus clair, voici le cas qui m'interesse plus particulierement :
nous avons une classe "iterator" qui me permet d'itérer sur une structure de
donnée. Un "iterator" a de bonnes raisons (qui ne sont pas discutables :))
d'être un peu lourd à copier. Maintenant imaginons une fonction
"nbElementsAfter" qui fait ca :

int nbElementsAfter(iterator& it)
{
int nb = 0;
while(it.next()) nb++;
for(int i=0; i<nb; i++) it.prev();
return nb;
}

Est-ce que cette fonction doit prendre un iterator& ou un const iterator& ?
Lequel de ces contrats vous semble le plus sensé pour une fonction qui prend
un const iterator& : "je ne modifierai pas l'iterateur" ou "l'iterateur
pointera au même endroit avant et apres l'appel" (ou encore autre chose ?) ?

Merci

--
Vincent

10 réponses

Avatar
Gabriel Dos Reis
"kanze" writes:

[...]

| Au fond, ce qui me gène le plus, c'est l'utilisation du mot
| itérateur dans la STL.

Pour comprendre le choix de la STL, il faut avoir compris son
histoire ; en particulier, avoir retenu qu'elle utilise « itérateur »
dans le sens de « abstraction d'itération », assez proche de celle de
CLU -- voir les ancêtres de la STL en Scheme --, acceptant l'absence
de semi-coroutine en C++ et l'implémentant avec les moyens du bord.

http://www.stepanovpapers.com/

(lire « High Order Imperative Programming » pour voir l'évolution des
idées)

| L'abstraction de la STL s'approche assez
| à celle des pointeurs classiques ; à cet égard, on pourrait
| arguer que le surcharge des opérateurs se justifie. Mais
| itérateur a toujours signifier une abstraction d'un niveau un
| peu plus élevé, où on ne parle plus en termes d'incrémenter ou
| de déréferencer, mais en termes d'avancer ou d'accéder à la
| valeur courante.

J'ai toujours compris les itérateurs de la STL de cette manière et
j'ai toujours lu la syntaxe de cette manière.

| Et encore moins en termes de comparer -- une
| itération est terminée, ou elle ne l'est pas, idépendamment de
| tout autre objet. Il y a une incohérence dans le vocabulaire :
| un « itérateur » ne supporte pas des opérateurs,

tu serais alors surpris de savoir que les pionniers qui ont inventé la
notion d'iterateur (et de coroutines, semi-cotourinees) ont toujours
pensé qu'ils supportaient des opérateurs.

for (iter current = first; current != last; ++current)

se lit « tant qu'on n'a pas atteint le dernier état de calcul,
considère l'état suivant »

| parce que les
| opérations fondamentales sur des itérateurs ne correspondent pas
| aux opérations fondamentales sur les types de base.
|
| Et comme toujours, ce n'est pas toujours un cas noir et blanc.
| Il y a bien des cas où l'abus est manifest : le cas de
| l'opérateur + unaire pour isDone,

nous parlons encore de la STL ?

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

"kanze" writes:

[...]

| Au fond, ce qui me gène le plus, c'est l'utilisation du mot
| itérateur dans la STL.

Pour comprendre le choix de la STL, il faut avoir compris son
histoire ; en particulier, avoir retenu qu'elle utilise « itérateur »
dans le sens de « abstraction d'itération », assez proche de celle de
CLU -- voir les ancêtres de la STL en Scheme --, acceptant l'absence
de semi-coroutine en C++ et l'implémentant avec les moyens du bord.

http://www.stepanovpapers.com/

(lire « High Order Imperative Programming » pour voir l'évolution des
idées)


Le Volume II des Notes for the Foundations of Programming
course n'est pas sans rapport non plus. Et peut-être plus
accessible à ceux ne connaissant pas scheme.

--
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:

| Gabriel Dos Reis writes:
|
| > "kanze" writes:
| >
| > [...]
| >
| > | Au fond, ce qui me gène le plus, c'est l'utilisation du mot
| > | itérateur dans la STL.
| >
| > Pour comprendre le choix de la STL, il faut avoir compris son
| > histoire ; en particulier, avoir retenu qu'elle utilise « itérateur »
| > dans le sens de « abstraction d'itération », assez proche de celle de
| > CLU -- voir les ancêtres de la STL en Scheme --, acceptant l'absence
| > de semi-coroutine en C++ et l'implémentant avec les moyens du bord.
| >
| > http://www.stepanovpapers.com/
| >
| > (lire « High Order Imperative Programming » pour voir l'évolution des
| > idées)
|
| Le Volume II des Notes for the Foundations of Programming
| course n'est pas sans rapport non plus. Et peut-être plus
| accessible à ceux ne connaissant pas scheme.

En effet.

La notion de « position » ou de « coordonnée » est celle utilisée en
Ada et expliqué dans leur papier « Generic Programming ».

-- Gaby
Avatar
Fabien LE LEZ
On 19 Jan 2006 01:36:17 -0800, "kanze" :

Utiliser == ou != pour isDone(), il n'y a aucun rapport ; c'est
de l'abus pur et simple.


Et " < ", c'est aussi de l'abus ?

Si on écrit

for (int i=0; i<5; ++i)

on peut bien écrire

for (int i=0; i != 5; ++i)

et, pourquoi pas,

for (iterator i= (quelque chose); i != (autre chose); ++i)

Au fond, ce qui me gène le plus, c'est l'utilisation du mot
itérateur dans la STL.

L'abstraction de la STL s'approche assez
à celle des pointeurs classiques


C'est même une généralisation pure et simple de la notion de pointeur.
À tel point que dans certaines implémentations,
std::vector<T>::iterator est (était ?) un T*.

Ce qui te gêne, c'est qu'on appelle ça "iterator", i.e. que le
vocabulaire du C++ ne corresponde pas au vocabulaire "classique" du
monde de la POO.
Franchement, c'est pas tellement plus gênant que le "vector" de C++
qui n'a rien à voir avec un vecteur.

Étant donné que tu connais trois langues, tu devrais savoir que deux
mots peuvent avoir des sens différents suivant le langage.
"Sensible" en anglais et "sensible" en français n'ont rien à voir, de
même que "vector" en C++ et "vector" (ou "vecteur") en maths, ou
encore "vector" (ou "vecteur") en médecine.

Avatar
kanze
Gabriel Dos Reis wrote:
"kanze" writes:

[...]

Au fond, ce qui me gène le plus, c'est l'utilisation du mot
itérateur dans la STL.


Pour comprendre le choix de la STL, il faut avoir compris son
histoire ; en particulier, avoir retenu qu'elle utilise
«@itérateur@» dans le sens de « abstraction d'itération »,
assez proche de celle de CLU -- voir les ancêtres de la STL en
Scheme --, acceptant l'absence de semi-coroutine en C++ et
l'implémentant avec les moyens du bord.


Je comprends son utilisation de itérateur, dans la contexte de
la STL. Je comprends (je crois) ce qu'il essayait à faire. À
vrai dire, je ne sais pas quel autre mot à proposer. Mais je
regrette fortement la confusion qui en résulte. D'un certain
sens, le mot itérateur était déjà pris.

[...]
Et encore moins en termes de comparer -- une itération est
terminée, ou elle ne l'est pas, idépendamment de tout autre
objet. Il y a une incohérence dans le vocabulaire : un «
itérateur » ne supporte pas des opérateurs,


tu serais alors surpris de savoir que les pionniers qui ont
inventé la notion d'iterateur (et de coroutines,
semi-cotourinees) ont toujours pensé qu'ils supportaient des
opérateurs.

for (iter current = first; current != last; ++current)

se lit « tant qu'on n'a pas atteint le dernier état de calcul,
considère l'état suivant »


Dans la contexte de la définition d'un langage nouveau, ça
pourrait éventuellement se défendre. Dans la contexte de C++, il
faut tenir compte des expectations des programmeurs C++,
conditionnées par les opérateurs de base.

Mais enfin, je parle là pour un développer lambda. Ce qui s'est
passé avec << et >>, que la plupart des programmeurs C++ les
considèrent des opérateurs d'insertion et d'extraction, pourrait
bien se passer pour les opérateurs de la STL -- je crois même
que c'est en train de se passer.

À la fin, c'est une question de notation, et la notation est
toujours conventionnelle. Violer les conventions, c'est mauvais.
Mais quand on est dans une position de définir les conventions
et les expectations ?

Disons que je crains simplement l'effet d'émulation. Dans la
contexte où l'auteur se trouvait alors, le choix de surcharger
<< et >> pour l'insertion et l'extraction pouvait très bien se
défendre : on était en train de définir un nouveau langage. Dans
la contexte de la STL, dans la mesure qu'elle est devenu une
partie de la norme, et en fait une partie integrante du C++, on
a aussi défini une convention et des expectations. Mais je ne
veux pas que le programmeur lambda prend ces exemples comme
excuse pour surcharger n'importe quoi n'importe comment. Le
surcharge est une arme à double tranchante : fait correctement,
il améliore énormement la lisabilité -- au point que je l'estime
nécessaire dans un langage bien conçu. Mais le moindre abus, et
on tombe très vite dans l'obfuscation.

parce que les opérations fondamentales sur des itérateurs ne
correspondent pas aux opérations fondamentales sur les types
de base.

Et comme toujours, ce n'est pas toujours un cas noir et
blanc. Il y a bien des cas où l'abus est manifest : le cas
de l'opérateur + unaire pour isDone,


nous parlons encore de la STL ?


Là non. C'est simplement le cas le plus flagrent de l'abus du
surcharge -- quelque chose d'affreux. Dans le cas de la STL,
j'estime qu'il y avait bien un peu d'abus, mais certainement pas
flagrent ou insupportable. Et le fait que c'est devenu la norme
« rédéfinit » les conventions du langage, et donc des
expectations du programmeur, ce qui rend l'« abus » sans
problèmes. À condition que les programmeurs ne prenent pas
l'exemple comme une excuse de faire n'importe quoi.

--
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


Avatar
Fabien LE LEZ
On 20 Jan 2006 01:22:38 -0800, "kanze" :

Dans le cas de la STL,
j'estime qu'il y avait bien un peu d'abus


Pourquoi ?

La règle dans la surcharge des opérateurs, c'est qu'ils fassent la
même chose que les opérateurs sur un type de base.
Généralement, on prend int comme "base", mais étant donné que le(s)
type(s) "iterator" de la STL sont faits pour généraliser la notion de
pointeur, avoir la même sémantique que les pointeurs ne me paraît pas
un abus.

On peut, par contre, estimer que le mot "iterator" est ici un abus,
mais ce n'est plus une histoire de surcharge.

Avatar
kanze
Fabien LE LEZ wrote:
On 19 Jan 2006 01:36:17 -0800, "kanze" :

Utiliser == ou != pour isDone(), il n'y a aucun rapport ;
c'est de l'abus pur et simple.


Et " < ", c'est aussi de l'abus ?


Si on le faisait entre deux itérateurs, oui.

Si on écrit

for (int i=0; i<5; ++i)


Ici, il s'agit d'un int. Le test final, ce n'est pas que j'ai
fini quoique ce soit. Le test, c'est si i est strictement
inférieur à 5.

C'est la différence dans le niveau d'abstraction. Dans la boucle
ci-dessus, même en supposant que je m'en sers pour itérer sur un
tableau de cinq éléments, ce que je manipule, c'est un entier,
non un itérateur. Le but, c'est peut-être d'itérer, mais je ne
me suis pas servi d'une abstraction d'itération, au moins, pas
en ce qui concerne les types.

on peut bien écrire

for (int i=0; i != 5; ++i)

et, pourquoi pas,

for (iterator i= (quelque chose); i != (autre chose); ++i)


Si « iterator » est un type numérique, pourquoi pas, en effet ?

Des deux choses une : ou bien, iterator est un type numérique
(ou -- en C++ -- quelque chose assimilable à un pointeur), et il
y a un très gros problème de nommage ; ou bien, iterator est une
implémentation d'une abstraction plus élevée, et la condition de
la fin, c'est qu'on a fini l'itération. Les opérateurs < ou ==
exprime des comparaisons sur des types numériques ou pointeurs,
ce sont des types qui peut servir pour itérer, mais ce ne sont
pas des itérateurs, en tant que tels. Et un itérateur a,
conceptuellement, dexu opérations : accéder à l'élément
courant et avancer, et un état visible à l'interface de
l'abstraction, le fait d'avoir fini ou non. (Encore que... on
pourrait discuter si l'accès fait partie réelement de
l'abstraction ; certains arguent, et je ne crois pas qu'ils ont
complètement tort, que l'accès doit être indépendant de
l'itération.)

Au fond, ce qui me gène le plus, c'est l'utilisation du mot
itérateur dans la STL.

L'abstraction de la STL s'approche assez à celle des
pointeurs classiques


C'est même une généralisation pure et simple de la notion de
pointeur.


Pas tout à fait non plus, mais AMHA, ils s'approchent bien plus
aux pointeurs du C++ qu'au concepte classique d'itérateur.

Au moins, tel que je le comprends. Gabi a eu raison de rappeler
qu'il y a eu d'autres définitions dans d'autres milieux. Mais la
définition que je donne, c'est bien celle qui était courante
dans les milieux C++ jusqu'au milieu des années 90, au moins. Et
elle correspond à une abstraction qui est encore fort utile, et
donc qui a encore besoin de son nom.

À tel point que dans certaines implémentations,
std::vector<T>::iterator est (était ?) un T*.

Ce qui te gêne, c'est qu'on appelle ça "iterator", i.e. que le
vocabulaire du C++ ne corresponde pas au vocabulaire
"classique" du monde de la POO.


Je crois que c'est bien ça. En concidérant aussi que cette
abstraction est encore valable aujourd'hui en C++, et qu'il faut
donc un nom pour en parler. Dans le monde du C++, cette
abstraction a pris le nom d'abord.

Maintenant, si tout le monde était d'accord pour un autre nom...
Mais tu vois ce qui c'est passé ici. Quelqu'un a posté une
classe avec « iterator » dans son nom, et certains se sont mis à
la critiquer parce qu'elle n'était pas conforme à la STL. Or que
le concepte qu'elle implémentait s'appelait « iterator » bien
avant qu'il y a eu une STL.

Franchement, c'est pas tellement plus gênant que le "vector"
de C++ qui n'a rien à voir avec un vecteur.


Sauf qu'il n'y a pas eu de concepte avant en C++ qui s'appelait
« vector ». Toujours est-il que le nom ne me plaît pas
particulièrement. Parce que je trouve que l'abstraction de
std::vector ressemble vraiment énormement à l'abstraction que
j'ai toujours appelée « array ». Au point que ça me gène un
peu -- dans le cas d'itérateur, on n'a qu'un seul nom pour deux
abstractions, et dans ce cas-ci, on a deux nom pour ce qui me
semble une seule abstraction.

Maintenant, si tu veux parler des cas où le nom ne convient
vraiment pas, uniquement sur la base de sa sémantique, parlons
de std::remove. (Et en passant, pourquoi dans la STL, il
s'appelle parfois remove, et d'autres fois erase ?)

Étant donné que tu connais trois langues, tu devrais savoir
que deux mots peuvent avoir des sens différents suivant le
langage. "Sensible" en anglais et "sensible" en français
n'ont rien à voir, de même que "vector" en C++ et "vector" (ou
"vecteur") en maths, ou encore "vector" (ou "vecteur") en
médecine.


Tout à fait.

Je sais aussi que même à l'intérieur d'une seule langue, un mot
peut avoir plusieurs significations distinctes : plus d'une
fois, je me suis senti gené en anglais par l'impossibilité de
faire la distinction entre « nombre » et « numéro ». Pas
tellement dans la langue parlée ou écrite, mais quand il
s'agissait de créer des noms de variables ou de types, dans un
langage de programmation. Ce qui est significatif en soi : ça ne
gène pas dans la langue même, mais ça gène quand je m'en sers
dans la programmation.

Enfin, c'est un problème. Ce n'est pas un grand problème dans la
mesure où personne n'oublie que le mot peut avoir plusieurs
significations, et où tout le monde le tient en compte, et se
pose la question d'abord, quelle est la signification voulue par
auteur ici, plutôt que de s'y lancer avec des critiques basées
sur le fait qu'il l'entend d'une façon, et que l'auteur
l'entendait de l'autre.

Même alors, évidemment, deux mots distincts seraient mieux.
Parce que moi aussi, force de me servir de la bibliothèque
standard tous les jours, quand je vois « XXX::iterator » come
nom d'un type en C++, je m'attends en premier abord à un
itérateur à la STL, et non ce que j'entends classiquement par
« itérateur ».

--
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


Avatar
Fabien LE LEZ
On 20 Jan 2006 05:58:32 -0800, "kanze" :

Quelqu'un a posté une
classe avec « iterator » dans son nom, et certains se sont mis à
la critiquer parce qu'elle n'était pas conforme à la STL.


Ben oui. Quelles que soient les erreurs commises par le comité, si tu
crées une classe qui a exactement le même nom qu'une classe de la STL,
le lecteur va s'attendre à ce qu'elle soit compatible.
Si ce n'est pas le cas, je conseille fortement de choisir un autre
nom -- ne serait-ce que "Iterator".

De même, si tu veux implémenter un vecteur (au sens mathématique du
terme), appeler ta classe "vector" ne fera qu'apporter de la
confusion.

En fait, j'ai une convention simple dans mon code : si un nom de type
ou de fonction ne commence pas par une majuscule, c'est qu'il a la
même sémantique qu'un type (ou une fonction) homonyme dans la SL.

Maintenant, si tu veux parler des cas où le nom ne convient
vraiment pas, uniquement sur la base de sa sémantique, parlons
de std::remove.


On va dire que le "re" est de trop.

(Et en passant, pourquoi dans la STL, il
s'appelle parfois remove, et d'autres fois erase ?)


Il y a des cas où "erase" n'efface pas des éléments ?

Ce n'est pas un grand problème dans la
mesure où personne n'oublie que le mot peut avoir plusieurs
significations, et où tout le monde le tient en compte, et se
pose la question d'abord, quelle est la signification voulue par
auteur ici,


Cf "méthode"...

Avatar
Gabriel Dos Reis
"kanze" writes:

[...]

| Mais enfin, je parle là pour un développer lambda.

Je n'aimerais pas me limiter l'expressivité et l'utilité d'une
bibliothèque sur des procès d'intention d'un programmeur lambda.

-- Gaby
Avatar
John Deuf
kanze :

Qui a dit quoique ce soit contre l'idée de surcharger les
opérateurs pour les objets. Ce qui n'est pas bien, c'est de
surcharger avec des significations arbitraires, sans rapport
avec la sémantique de l'opérateur de base. À la longue, ça rend
les programmes illisibles.


Alors qu'est-ce que tu proposerais pour remplacer les operateurs << et >>
sur les stream ?
Parce que dans ce contexte, il me semble qu'ils n'ont aucun rapport avec
le decalage de bit. (Et je ne trouve pas qu'ils rendent le programme
illisible.)

--
John Deuf