OVH Cloud OVH Cloud

[Vider un std::vector] swap

32 réponses
Avatar
Fabien LE LEZ
Bonjour,

J'aimerais savoir s'il y a une différence entre

vector<C> v;
...
vector<C>().swap (v);

et

vector<C> v;
...
v.swap (vector<C>());

Merci d'avance...


--
schtroumpf schtroumpf
Jean-Emile de France

10 réponses

1 2 3 4
Avatar
Fabien LE LEZ
On Sun, 11 Jul 2004 19:44:22 +0200, Loïc Joly
:

Et que penses-tu de l'écriture :
std::swap(v, vector<T>());

Elle me semble avoir l'avantage de ne favoriser aucun des termes


Outre le fait de ne pas fonctionner, ni de ne pas faire appel à
std::vector<>::swap(), elle a le gros inconvénient de ne favoriser
aucun des deux termes -- alors que justement on cherche à favoriser v.

Avatar
kanze
Fabien LE LEZ wrote in message
news:...

J'aimerais savoir s'il y a une différence entre

vector<C> v;
...
vector<C>().swap (v);

et

vector<C> v;
...
v.swap (vector<C>());


Le premier est légal, et le deuxième non.

Est-ce que ça te semble une différence suffisante ?

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
Loïc Joly wrote in message
news:<ccru10$tih$...
drkm wrote:
Mais je trouve tout de même que lorsque l'on a un objet A a
modifier, et un objet B créé pour le modifier, plus clair d'écrire

A.swap( B ) ;

que

B.swap( A ) ;


<Disclaimer>
Je n'ai que survolé la discussion, désolé si je reviens sur un point
déjà mentionné)
</Disclaimer>

Et que penses-tu de l'écriture :
std::swap(v, vector<T>());


Que c'est illégal.

En fait, s'il s'agissait de la lisibilité : v.clear(). Une fonction avec
un nom comme clear() doit remettre l'objet à l'état qu'il avait en
sortie du constructeur.

Mais le nommage des fonctions n'est pas le point fort de la STL.

En attendant, à côté de begin et end, j'ai bien :

template< typename Container >
void
clear( Container& c )
{
Container().swap( c ) ;
}

Faute d'avoir ce que je veux avec v.clear(), je l'ai avec clear( v ).

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Amerio
En fait, s'il s'agissait de la lisibilité : v.clear(). Une fonction
avec un nom comme clear() doit remettre l'objet à l'état qu'il avait
en sortie du constructeur.


Je ne suis pas trop d'accord avec toi, là.
Pour moi, clear fait bien ce qu'il dit : il vide le conteneur. Une fonction
doit faire ce que dit son nom, et rien de plus.
S'il devait exister uen fonction devant remettre l'objet tel qu'apres le
constructeur, j'imaginerais plutot un nom comme reset().
(mais ya pas. D'ou le "hack" avec swap)

Mais le nommage des fonctions n'est pas le point fort de la STL.
Entierement d'accord là !

Mais il est trop tard pour revenir dessus toutes ces erreurs (empty, clear,
std::remove, etc... pour les plus connus)

Avatar
Fabien LE LEZ
On 12 Jul 2004 01:45:42 -0700, :

En fait, s'il s'agissait de la lisibilité : v.clear().


Ou éventuellement, "v= std::vector<Machin>()".

Avatar
drkm
Fabien LE LEZ writes:

On 12 Jul 2004 01:45:42 -0700, :

En fait, s'il s'agissait de la lisibilité



Non. Le sous-fil était parti sur la manière la plus naturelle
d'écrire entre A.swap( B ) et B.swap( A ) pour les définitions de A et
B données plus haut dans le fil, indépendamment du sujet initial de
vider un vecteur.

: v.clear().



Pour vider un vecteur, avec la sémantique du post initial,
std::vector<T>.swap( v ), je trouve cela extrêmement illisible.

Ou éventuellement, "v= std::vector<Machin>()".


Encore une fois, pas pour la sémantique de ton article initial.

~> cat fclcxx.cc
#include <iostream>
#include <vector>

int main()
{
typedef std::vector< int > Vector ;
typedef Vector::size_type Size ;

Vector v1( Size( 10 ) ) ;
Vector v2( Size( 10 ) ) ;

Vector().swap( v1 ) ;
v2 = Vector() ;

std::cout
<< "v1 capacity : "
<< v1.capacity()
<< ", v2 capacity : "
<< v2.capacity()
<< std::endl ;
}
~> g++ -o fclcxx fclcxx.cc -Wall -ansi -pedantic
~> ./fclcxx
v1 capacity : 0, v2 capacity : 10

Je sais que cela ne prouve rien d'autre que ce que fait cette
version de ce compilateur.

Mais pour résumer ce que j'ai compris de cette discussion, ainsi il
me semble que d'un autre fil d'il y a quelques jours (et en y ajoutant
quelques déductions personnelles) :

· la norme ne fournit pas d'autre moyen d'abaisser la capacité
d'un vecteur que par reserve() ;

· cela est incompatible avec la contrainte de complexité constante
de swap() ;

· un DR (le 101, je pense) recommande std::vector<T>().swap( v )
pour vider un vecteur, dans le sens d'abaisser sa capacité à 0 ;

· l'inplémentation que j'imagine triviale de swap() modifie la
capacité, pas celle de operator=().

Pour ces raisons, je pense que la manière la plus sûre de faire [*]
est bien :

std::vector<T>().swap( v ) ;

[*] La seule un peu sûre, bien qu'en contradiction avec la norme,
contradiction aténuée par le fait qu'elle est levée par le DR
101 et que la norme est contradictoire sur ce point.

--drkm


Avatar
kanze
"Amerio" wrote in message
news:<40f282a4$0$24437$...

En fait, s'il s'agissait de la lisibilité : v.clear(). Une fonction
avec un nom comme clear() doit remettre l'objet à l'état qu'il avait
en sortie du constructeur.


Je ne suis pas trop d'accord avec toi, là. Pour moi, clear fait bien
ce qu'il dit : il vide le conteneur. Une fonction doit faire ce que
dit son nom, et rien de plus. S'il devait exister uen fonction devant
remettre l'objet tel qu'apres le constructeur, j'imaginerais plutot un
nom comme reset(). (mais ya pas. D'ou le "hack" avec swap)


Dans l'anglais technique, les mots « clear » et « reset » sont des
synonymes. (Dans l'anglais non technique, « clear » signifie plutôt
transparent ou clair, comme couleur. Et j'ai déjà vue un document
technique ou on avait traduit « master clear » par « maître
clair ». Évidemment, le traducteur n'y avait rien compris.)

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
Fabien LE LEZ wrote in message
news:...
On 12 Jul 2004 01:45:42 -0700, :

En fait, s'il s'agissait de la lisibilité : v.clear().


Ou éventuellement, "v= std::vector<Machin>()".


Tout à fait. Je me serais attendu à ce que l'affectation puisse
invalider tous les itérateurs, ET toutes les autres garanties que
pouvait donner l'objet précédemment.

En fait, je trouve les garanties qui empêche le vidage avec clear() ou
l'affectation un peu subtiles. Parce que les deux fonctions invalides
bien tous les itérateurs existants. Seulement, je ne sais pas vraiment
formuler une garantie moins contraignante qui couvre ce que je veux
garantir, mais qui ne couvre pas les cas subtiles que j'aimerais laisser
de côté.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Gabriel Dos Reis
writes:

| "Amerio" wrote in message
| news:<40f282a4$0$24437$...
|
| > > En fait, s'il s'agissait de la lisibilité : v.clear(). Une fonction
| > > avec un nom comme clear() doit remettre l'objet à l'état qu'il avait
| > > en sortie du constructeur.
|
| > Je ne suis pas trop d'accord avec toi, là. Pour moi, clear fait bien
| > ce qu'il dit : il vide le conteneur. Une fonction doit faire ce que
| > dit son nom, et rien de plus. S'il devait exister uen fonction devant
| > remettre l'objet tel qu'apres le constructeur, j'imaginerais plutot un
| > nom comme reset(). (mais ya pas. D'ou le "hack" avec swap)
|
| Dans l'anglais technique, les mots « clear » et « reset » sont des
| synonymes.

Je ne disputerais ton origine, mais en termes techniques « clear »,
c'est effacer des marques d'un certain nombre d'état alors que « reset »
c'est remettre dans l'état de virginité. Ce n'est pas pareil.

-- Gaby
Avatar
kanze
drkm wrote in message
news:...

Fabien LE LEZ writes:

On 12 Jul 2004 01:45:42 -0700, :

En fait, s'il s'agissait de la lisibilité



Non. Le sous-fil était parti sur la manière la plus naturelle
d'écrire entre A.swap( B ) et B.swap( A ) pour les définitions de A et
B données plus haut dans le fil, indépendamment du sujet initial de
vider un vecteur.


Sauf que s'il ne s'agit pas de vider un vecteur, il n'y a pas de
problème, parce qu'on aurait deux objets, et A.swap( B ), B.swap( A ) et
swap( A, B ) sont tous valables.

: v.clear().



Pour vider un vecteur, avec la sémantique du post initial,
std::vector<T>.swap( v ), je trouve cela extrêmement illisible.


Tu veux dire std::vector<T>().swap( v ), je crois.

Nous sommes d'accord avec toi, et c'est la thème de notre discussion (un
sous-sous-thread ?).

Ou éventuellement, "v= std::vector<Machin>()".


Encore une fois, pas pour la sémantique de ton article initial.


Nous savons que ça ne fait pas ce qu'on veut. Ce dont on parle, c'est ce
qui aurait du être (à notre avis, évidemment). En ce qui me concerne,
intuitivement, je ne m'attendrais à ce qu'aucune garantie précédente ne
vaut après un appel à une fonction qui s'appelle clear, ou après une
affectation. Mais je sais que le comité en a jugé autrement (suite
peut-être au contraint important qu'il fallait rester le plus près
possible à ce qui était déjà écrit).

~> cat fclcxx.cc
#include <iostream>
#include <vector>

int main()
{
typedef std::vector< int > Vector ;
typedef Vector::size_type Size ;

Vector v1( Size( 10 ) ) ;
Vector v2( Size( 10 ) ) ;

Vector().swap( v1 ) ;
v2 = Vector() ;

std::cout
<< "v1 capacity : "
<< v1.capacity()
<< ", v2 capacity : "
<< v2.capacity()
<< std::endl ;
}
~> g++ -o fclcxx fclcxx.cc -Wall -ansi -pedantic
~> ./fclcxx
v1 capacity : 0, v2 capacity : 10

Je sais que cela ne prouve rien d'autre que ce que fait cette
version de ce compilateur.


Ça correspond à peu près à ce qu'a décidé le comité.

En gros, d'après le peu que je sais, il y a eu deux variants répandus,
ce que faisait l'implémentation SGI et ces dérivées (dont la STLport et
g++), et ce que faisait Dinkumware. (Je ne sais pas ce que faisait Rogue
Wave.) À la fin, le comité a décidé de suivre ce que faisait SGI. Je ne
connais pas les raisonnements qui ont été invoqués, mais je peux bien en
penser à deux possibles :

- l'implémentation SGI est, en fin de compte, l'implémentation
« référence », qui dérive le plus directement de ce qu'a fait
Stepenov au départ, et

- l'implémentation SGI représentait le moindre changement dans la
norme -- on supprimait une garantie rélative à swap, tandis que
l'implémentation Dinkumware le supprimait à la fois pour swap et
pour clear.

Mais pour résumer ce que j'ai compris de cette discussion, ainsi il
me semble que d'un autre fil d'il y a quelques jours (et en y ajoutant
quelques déductions personnelles) :

· la norme ne fournit pas d'autre moyen d'abaisser la capacité
d'un vecteur que par reserve() ;


La norme ne permet pas à reserve() de baisser la capacité. La
post-condition de reserver( n ) est :

capacity() >= max( old.capacity(), n )

· cela est incompatible avec la contrainte de complexité constante
de swap() ;


Ce n'est pas là le problème. Le problème réel concerne la validité des
itérateurs. Si on ne considère que la norme, telle qu'elle était en
1998, on a pour un vector< int > v :

v.reserve( 100 ) ;
v.clear() ; // ou : vector<int>().swap( v ) ;
v.push_back( 0 ) ; v.push_back( 1 ) ; v.push_back( 2 ) ;
vector< int >::iterator i = v.begin() + 1 ;
for ( int i = 3 ; i < 100 ; ++ i ) {
v.push_back( i ) ;
}

Selon la norme, on a ici la garantie que l'itérateur i est encore
valide. Ce qui est impossible dans le cas de swap si on veut respecter
les contraints de complexité.

· un DR (le 101, je pense) recommande std::vector<T>().swap( v )
pour vider un vecteur, dans le sens d'abaisser sa capacité à 0 ;

· l'inplémentation que j'imagine triviale de swap() modifie la
capacité, pas celle de operator=().


Un vector se caractèrise par un nombre d'attributes, dont la capacité.
L'implémentation normale de swap échangerait toutes ces attributes.

L'implémentation « normale » de l'affectation utiliserait swap. Sauf que
c'est interdit, parce que dans l'exemple ci-dessus, si je remplace
v.clear() par v = vector<int>(), j'ai toujours la garantie sur
l'itérateur. La TC a enlevé la garantie pour swap, et uniquement pour
swap.

En fait, l'affectation d'un vecteur ne modifie pas toutes ces
attributes. L'affectation est en fait plutôt une affectation des
éléments seulement ; capacity() n'y est pas concerné (sauf que si la
capacity() du vector cible n'est pas suffisante, il faut l'augmenter,
évidemment).

Pour ces raisons, je pense que la manière la plus sûre de faire [*]
est bien :

std::vector<T>().swap( v ) ;

[*] La seule un peu sûre, bien qu'en contradiction avec la norme,
contradiction aténuée par le fait qu'elle est levée par le DR
101 et que la norme est contradictoire sur ce point.


C'est la seule façon selon la TC. C'est la seule façon qui n'a jamais
marché avec l'implémentation SGI.

Mais ce n'est pas lisible. C'est pour ça qu'on discute.

--
James Kanze GABI Software http://www.gabi-soft.fr
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



1 2 3 4