Je cherche un moyen de réinitialiser la longueur d'une string
à 0 sans toucher à sa capacity(). Que dit la norme à ce
propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
Je cherche un moyen de réinitialiser la longueur d'une string
à 0 sans toucher à sa capacity(). Que dit la norme à ce
propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
Je cherche un moyen de réinitialiser la longueur d'une string
à 0 sans toucher à sa capacity(). Que dit la norme à ce
propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
Bonjour,
Je cherche un moyen de réinitialiser la longueur d'une string à 0 sans
toucher à sa capacity().
Que dit la norme à ce propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
Bonjour,
Je cherche un moyen de réinitialiser la longueur d'une string à 0 sans
toucher à sa capacity().
Que dit la norme à ce propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
Bonjour,
Je cherche un moyen de réinitialiser la longueur d'une string à 0 sans
toucher à sa capacity().
Que dit la norme à ce propos ? Quel sont les influences de:
- clear()
- resize( 0 )
- erase( begin(), end() )
sur la valeur de capacity() ?
- la string est contruite au moyen de pas mal de concaténations
- la string est contruite au moyen de pas mal de concaténations
- la string est contruite au moyen de pas mal de concaténations
Je vais ajouter quelques précisions. C'est bien std::string
qu'il me faut, utilisé comme paramètre out :
void DoSomething( std::string & Result )
{
Result.clear();
Result.reserve( /* ce qu'il faut */ );
for ( ... )
{
Result.append( ... );
}
}
std::string res;
while ( /* pas mal de fois */ )
{
DoSomething( res );
// ...
}
Vu que le cas typique d'utilisation, c'est le même std::string
qui sert de multiples fois comme paramètre out, et que :
- la string est contruite au moyen de pas mal de concaténations
- elle a régulièrement la même longueur une fois construite
l'idée est de profiter de la place allouée lors des
traitements précédents. Ma crainte est que le clear()
désalloue la mémoire, et donc d'avoir une allocation en
suivant avec reserve().
Mon programme marche, et j'ai même envie de dire que cette
optimisation ne se verra pas. C'est donc essentiellement par
curiosité. Car je peux simplement faire un resize() suivi de
copies, mais les append() rend mon code plus simple.
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
Je vais ajouter quelques précisions. C'est bien std::string
qu'il me faut, utilisé comme paramètre out :
void DoSomething( std::string & Result )
{
Result.clear();
Result.reserve( /* ce qu'il faut */ );
for ( ... )
{
Result.append( ... );
}
}
std::string res;
while ( /* pas mal de fois */ )
{
DoSomething( res );
// ...
}
Vu que le cas typique d'utilisation, c'est le même std::string
qui sert de multiples fois comme paramètre out, et que :
- la string est contruite au moyen de pas mal de concaténations
- elle a régulièrement la même longueur une fois construite
l'idée est de profiter de la place allouée lors des
traitements précédents. Ma crainte est que le clear()
désalloue la mémoire, et donc d'avoir une allocation en
suivant avec reserve().
Mon programme marche, et j'ai même envie de dire que cette
optimisation ne se verra pas. C'est donc essentiellement par
curiosité. Car je peux simplement faire un resize() suivi de
copies, mais les append() rend mon code plus simple.
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
Je vais ajouter quelques précisions. C'est bien std::string
qu'il me faut, utilisé comme paramètre out :
void DoSomething( std::string & Result )
{
Result.clear();
Result.reserve( /* ce qu'il faut */ );
for ( ... )
{
Result.append( ... );
}
}
std::string res;
while ( /* pas mal de fois */ )
{
DoSomething( res );
// ...
}
Vu que le cas typique d'utilisation, c'est le même std::string
qui sert de multiples fois comme paramètre out, et que :
- la string est contruite au moyen de pas mal de concaténations
- elle a régulièrement la même longueur une fois construite
l'idée est de profiter de la place allouée lors des
traitements précédents. Ma crainte est que le clear()
désalloue la mémoire, et donc d'avoir une allocation en
suivant avec reserve().
Mon programme marche, et j'ai même envie de dire que cette
optimisation ne se verra pas. C'est donc essentiellement par
curiosité. Car je peux simplement faire un resize() suivi de
copies, mais les append() rend mon code plus simple.
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
On Tue, 23 Aug 2005 09:58:27 +0200, Aurelien Regat-Barrel
:- la string est contruite au moyen de pas mal de concaténations
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
On Tue, 23 Aug 2005 09:58:27 +0200, Aurelien Regat-Barrel
<nospam.aregatba@yahoo.fr>:
- la string est contruite au moyen de pas mal de concaténations
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
On Tue, 23 Aug 2005 09:58:27 +0200, Aurelien Regat-Barrel
:- la string est contruite au moyen de pas mal de concaténations
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
Ça dépend de l'implémentation, évidemment, mais pour toutes les
implémentations que je connais, ça serait une pessimismation,
souvent même de beaucoup.
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
Ça dépend de l'implémentation, évidemment, mais pour toutes les
implémentations que je connais, ça serait une pessimismation,
souvent même de beaucoup.
Utiliser un ostringstream ou un ostrstream comme intermédiaire
dans ce cas (et donc "operator<<" à la place de "append"),
serait-ce une optimisation ? une pessimisation ? aucun des
deux ?
Ça dépend de l'implémentation, évidemment, mais pour toutes les
implémentations que je connais, ça serait une pessimismation,
souvent même de beaucoup.
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
Aucune, je crois. Du point de vue de la norme ; dans une
implémentation donnée, évidemment, ça peut en faire une.
En gros, la seule chose, c'est d'implémenter un benchmark, et
comparer des valeurs réeles. En particulier :
-- Si l'implémentation se sert du comptage des références et de
copie à l'écriture, la solution optimale pourrait bien
dépendre de ce que tu fais avec la chaîne res dans ta
boucle. En règle générale (mais avec beaucoup d'exceptions),
tu as intérêt à limiter la portée de chaque instance la plus
possible, pour éviter des partages réels, tandis que les
copies en soi ne sont pas chères. (Si, par exemple, dans la
boucle, tu as affecté res à une autre variable qui existe
encore, ou clear() ou resize() va forcément réalloue de la
mémoire.
-- Si l'implémentation utilise ce qu'on appelle l'optimisation
pour les petites chaînes (c'est le cas des implémentations
Microsoft à partir de la version 7, je crois), il est
prèsque sur que clear() ou resize( 0 ) libère la mémoire
allouer, pour ramener la capacité à sa valeur minimum (qui
sera 8, 16, 32, ou quelque chose du genre).
S'il faut vraiment éviter l'allocation, la solution que je vois
serait de déclarer la chaîne en dehors de la boucle, en
l'initialisant avec une chaîne de la bonne longueur, et utiliser
les itérateurs dans DoSomething :
void
DoSomething( std::string::iterator dest )
{
dest = std::copy( s.begin(), s.end(), dest ) ;
// à la place de « append( s ) »...
}
std::string res( longueurVoulue, ' ' ) ;
while ( ... ) {
DoSomething( res.begin() ) ;
// ...
}
Mais même ici, dans le cas d'une implémentation copie à
l'écriture, il faudrait s'assurer que l'implémentation n'est
jamais partagée.
Mais je n'y irais que si le profiler me disait qu'il fallait (au
moins que DoSomething soit assez général et assez simple qu'il y
a un intérêt à en faire un template, sur le type d'itérateur).
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
Aucune, je crois. Du point de vue de la norme ; dans une
implémentation donnée, évidemment, ça peut en faire une.
En gros, la seule chose, c'est d'implémenter un benchmark, et
comparer des valeurs réeles. En particulier :
-- Si l'implémentation se sert du comptage des références et de
copie à l'écriture, la solution optimale pourrait bien
dépendre de ce que tu fais avec la chaîne res dans ta
boucle. En règle générale (mais avec beaucoup d'exceptions),
tu as intérêt à limiter la portée de chaque instance la plus
possible, pour éviter des partages réels, tandis que les
copies en soi ne sont pas chères. (Si, par exemple, dans la
boucle, tu as affecté res à une autre variable qui existe
encore, ou clear() ou resize() va forcément réalloue de la
mémoire.
-- Si l'implémentation utilise ce qu'on appelle l'optimisation
pour les petites chaînes (c'est le cas des implémentations
Microsoft à partir de la version 7, je crois), il est
prèsque sur que clear() ou resize( 0 ) libère la mémoire
allouer, pour ramener la capacité à sa valeur minimum (qui
sera 8, 16, 32, ou quelque chose du genre).
S'il faut vraiment éviter l'allocation, la solution que je vois
serait de déclarer la chaîne en dehors de la boucle, en
l'initialisant avec une chaîne de la bonne longueur, et utiliser
les itérateurs dans DoSomething :
void
DoSomething( std::string::iterator dest )
{
dest = std::copy( s.begin(), s.end(), dest ) ;
// à la place de « append( s ) »...
}
std::string res( longueurVoulue, ' ' ) ;
while ( ... ) {
DoSomething( res.begin() ) ;
// ...
}
Mais même ici, dans le cas d'une implémentation copie à
l'écriture, il faudrait s'assurer que l'implémentation n'est
jamais partagée.
Mais je n'y irais que si le profiler me disait qu'il fallait (au
moins que DoSomething soit assez général et assez simple qu'il y
a un intérêt à en faire un template, sur le type d'itérateur).
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Je sais que le clear() de ma STL (VC++ 7.1) fait un erase(
begin(), end() ), et que la prochaine version (VC++ 8) fera un
resize( 0 ). Je me demandais du point de vue de la norme
quelle était la nuance...
Aucune, je crois. Du point de vue de la norme ; dans une
implémentation donnée, évidemment, ça peut en faire une.
En gros, la seule chose, c'est d'implémenter un benchmark, et
comparer des valeurs réeles. En particulier :
-- Si l'implémentation se sert du comptage des références et de
copie à l'écriture, la solution optimale pourrait bien
dépendre de ce que tu fais avec la chaîne res dans ta
boucle. En règle générale (mais avec beaucoup d'exceptions),
tu as intérêt à limiter la portée de chaque instance la plus
possible, pour éviter des partages réels, tandis que les
copies en soi ne sont pas chères. (Si, par exemple, dans la
boucle, tu as affecté res à une autre variable qui existe
encore, ou clear() ou resize() va forcément réalloue de la
mémoire.
-- Si l'implémentation utilise ce qu'on appelle l'optimisation
pour les petites chaînes (c'est le cas des implémentations
Microsoft à partir de la version 7, je crois), il est
prèsque sur que clear() ou resize( 0 ) libère la mémoire
allouer, pour ramener la capacité à sa valeur minimum (qui
sera 8, 16, 32, ou quelque chose du genre).
S'il faut vraiment éviter l'allocation, la solution que je vois
serait de déclarer la chaîne en dehors de la boucle, en
l'initialisant avec une chaîne de la bonne longueur, et utiliser
les itérateurs dans DoSomething :
void
DoSomething( std::string::iterator dest )
{
dest = std::copy( s.begin(), s.end(), dest ) ;
// à la place de « append( s ) »...
}
std::string res( longueurVoulue, ' ' ) ;
while ( ... ) {
DoSomething( res.begin() ) ;
// ...
}
Mais même ici, dans le cas d'une implémentation copie à
l'écriture, il faudrait s'assurer que l'implémentation n'est
jamais partagée.
Mais je n'y irais que si le profiler me disait qu'il fallait (au
moins que DoSomething soit assez général et assez simple qu'il y
a un intérêt à en faire un template, sur le type d'itérateur).
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Mais dans mon cas DoSomething() renvoie un booléen = erreur ou pas.
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Mais dans mon cas DoSomething() renvoie un booléen = erreur ou pas.
Le problème, c'est que les implémentations de std::string
varient beaucoup. Avec g++, au moins,
std::string
DoSomething()
{
std::string result ;
result.reserve( /* ce qu'il faut */ ) ;
for ( ... ) {
result.append( ... ) ;
}
return result ;
}
while ( /* pas mal de fois */ ) {
std::string res( DoSomething() ) ;
// ...
}
serait probablement plus rapide de ta version (encore que ce
n'est pas sûr -- tout dépend de beaucoup de choses qu'on ne
maîtrise pas).
Mais dans mon cas DoSomething() renvoie un booléen = erreur ou pas.
Tiens, ça fait longtemps que je n'en ai pas parlé. C'est pour ça
qu'il existe Fallible<T> ; on renvoie Fallible< std::string >,
et le tour est joué. C'est même plus robuste, puisque tout essai
à accéder à la chaîne si la variable de contrôle est fausse
donne une violation d'une assertion.
Tiens, ça fait longtemps que je n'en ai pas parlé. C'est pour ça
qu'il existe Fallible<T> ; on renvoie Fallible< std::string >,
et le tour est joué. C'est même plus robuste, puisque tout essai
à accéder à la chaîne si la variable de contrôle est fausse
donne une violation d'une assertion.
Tiens, ça fait longtemps que je n'en ai pas parlé. C'est pour ça
qu'il existe Fallible<T> ; on renvoie Fallible< std::string >,
et le tour est joué. C'est même plus robuste, puisque tout essai
à accéder à la chaîne si la variable de contrôle est fausse
donne une violation d'une assertion.