IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Sémantique de mouvement & style de codage


Sujet :

Langage C++

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut Sémantique de mouvement & style de codage
    Hello,

    Soit la fonction suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void maFonction(std::vector<int>& monVecteur)
    {
        // vaut-il mieux :
        monVecteur.resize(10000);
        // ou
        monVecteur = std::vector<int>(10000);
    }
    J'aurais tendance à utiliser la première forme, mais étant tombé sur la deuxième forme dans le code d'un collègue, j'aurais un peu de mal à argumenter (on va juste réallouer les quelques octets des "métadonnées" du vecteur).

  2. #2
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Hello,

    Il faut voir l'état actuel du vecteur, la deuxième solution évite d'avoir un vecteur trop grand (mémoire réservée). Mais en dehors de ce cas un peu particulier je préférerais la première forme aussi.

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    À l'entrée de la fonction, le vecteur est vide (c'est une valeur de retour).

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,
    Citation Envoyé par oodini Voir le message
    À l'entrée de la fonction, le vecteur est vide (c'est une valeur de retour).
    Heuu... en est tu vraiment sur et certain

    Si oui (ou si tu considère que cela doit forcément être le cas), il serait intéressant d'expliciter cette précondition par une assertion car, si ce n'est pas le cas, c'est que tu es face à une erreur de la part de l'utilisateur de la fonction.

    Si non (ou si on ne considère pas que c'est forcément le cas), tu ne peux simplement pas partir du principe que ce sera le cas...
    Citation Envoyé par Iradrille Voir le message
    Hello,

    Il faut voir l'état actuel du vecteur, la deuxième solution évite d'avoir un vecteur trop grand (mémoire réservée). Mais en dehors de ce cas un peu particulier je préférerais la première forme aussi.
    A vrai dire, tab.resize(N) et tab = std::vector<int>(N) auront tous les deux le même résultat :définir la taille du tableau à la valeur indiquée...

    Ce qui ne ferait que modifier la capacité (comprend : le nombre d'élément maximum avant de devoir redemander de l'espace supplémentaire), ce serait le code tab.reserve(N).

    Dés lors, en dehors de toute certitude que le tableau est bel et bien vide -- et j'irais même jusqu'à dire "et même si le contrat selon lequel le tableau doit être vide était explicitement vérifié" -- je pencherais de préférence pour l'utilisation de resize(), simplement parce que:
    1. cela évite de perdre plus de données que nécessaire dans la bataille au cas où le tableau n'est pas vide
    2. j'estime qu'il ne sert à rien de passer par une variable temporaire alors que la classe fournit "tout ce qu'il faut" pour obtenir un résultat similaire

    Alors, bien sur, il y a le fameux argument "oui, mais grace à la sémantique de mouvement, cela revient malgré tout au même", il faut savoir que, même si la sémantique de mouvement permet d'aller beaucoup plus vite que la copie "normale", elle implique quand même toujours la "canibalisation" de la variable temporaire et que cette "canibalisation" (en fait, un std::swap sur l'ensemble des membres de la classe, à peu de chose près ) prendra de toutes façons plus de temps que de redéfinir "tout simplement" les bonnes valeurs pour la variable qui existe déjà...

    Bien sur, on parle de "quelques fréquences d'horloge en plus ou en moins", mais... elles seront bel et bien utilisées
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 146
    Billets dans le blog
    4
    Par défaut
    En fait tes 2 écritures sont identiques dans le résultat.
    Dans le 2° cas, tu crées un temporaire puis utilises l'opérateur =, possiblement optimisé par le compilo.
    Mais la 1° écriture est préférable dans tous les cas.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par Bousk Voir le message
    En fait tes 2 écritures sont identiques dans le résultat.
    Dans le 2° cas, tu crées un temporaire puis utilises l'opérateur =, possiblement optimisé par le compilo.
    Mais la 1° écriture est préférable dans tous les cas.
    Merci de confirmer ce que j'ai écrit
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par koala01 Voir le message
    A vrai dire, tab.resize(N) et tab = std::vector<int>(N) auront tous les deux le même résultat :définir la taille du tableau à la valeur indiquée...

    Ce qui ne ferait que modifier la capacité (comprend : le nombre d'élément maximum avant de devoir redemander de l'espace supplémentaire), ce serait le code tab.reserve(N).
    tab.resize(N); ne va pas réduire la mémoire réservée au vecteur (faudra un shrink_to_fit).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void maFonction(std::vector<int>& monVecteur)
    {
        monVecteur.resize(10000); // si monVecteur.capacity() > 10000 alors on garde un vecteur "trop grand"
        // "un peu trop grand" -> osef, "beaucoup trop grand" -> ça peut être problématique
        // -> mémoire réservée au vecteur pas libérée pour le moment
    }
    C'est généralement pas un problème (et ici le vecteur est vide en entrée), mais ça peut l'être.

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    tab.resize(N); ne va pas réduire la mémoire réservée au vecteur (faudra un shrink_to_fit).
    Je ne suis pas sur, mais il me semble que c'est implementation defined (en tout cas, ca l'est pour std::string )... j'irai vérifier dans la norme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void maFonction(std::vector<int>& monVecteur)
    {
        monVecteur.resize(10000); // si monVecteur.capacity() > 10000 alors on garde un vecteur "trop grand"
        // "un peu trop grand" -> osef, "beaucoup trop grand" -> ça peut être problématique
        // -> mémoire réservée au vecteur pas libérée pour le moment
    }
    C'est généralement pas un problème (et ici le vecteur est vide en entrée), mais ça peut l'être.
    Ben, que le tableau soit vide en entrée ou non, il est clair que celui qui décide d'effectuer un resize (ou un reserve, d'ailleurs) basé sur un nombre magique prend énormément de risques : si son nombre magique est "très près" du nombre final d'éléments, tout va bien; mais, plus il s'en écarte, plus il prend le risque soit d'avoir prévu (beaucoup) trop d'espace, soit de n'avoir limité "qu'un tout petit peu" le nombre d'augmentation de taille qui seront effectués

    Car il faut bien dire que le problème de la taille n'est pas uniquement limité au fait que la taille soit... trop importante
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 146
    Billets dans le blog
    4
    Par défaut
    Il semblerait. C'est bien préciser dans le cas où n dépasse la capacité actuelle, mais pas dans le cas où il est inférieur.
    http://www.cplusplus.com/reference/v...vector/resize/
    Resizes the container so that it contains n elements.

    If n is smaller than the current container size, the content is reduced to its first n elements, removing those beyond (and destroying them).

    If n is greater than the current container size, the content is expanded by inserting at the end as many elements as needed to reach a size of n. If val is specified, the new elements are initialized as copies of val, otherwise, they are value-initialized.

    If n is also greater than the current container capacity, an automatic reallocation of the allocated storage space takes place.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    En fait, c'est ca : la capacité n'est jamais modifiée par une diminution de la taille (à moins d'appeler shrink_to_fit), mais une diminution de la taille détruit forcément les éléments devenus "surnuméraires"...

    Un petit exemple pour s'en convaincre (très très verbeux, je dois bien l'avouer )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #include <iostream>
    #include <vector>
    int main(){
        {
            std::vector<int> tab(10000);
            std::cout<<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n"
                     <<"erasing 9990 elements\n";
            tab.erase(tab.begin()+10, tab.end());
            std::cout<<"no surprize :\n"
                     <<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n"
                     <<"resizing...\n";
            tab.resize(10);
            std::cout<<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n"
                     <<"shrinking..\n";
            tab.shrink_to_fit();
            std::cout<<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n";
        }
        {
            std::vector<int> tab(10000);
            std::cout<<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n"
                     <<"resizing\n";
            tab.resize(10);
            std::cout<<"capacity : "<<tab.capacity()<<"\n"
                     <<"size : "<<tab.size()<<"\n";
        }
    }
    Le premier bloc d'instructions démontre que, quoi que l'on fasse, la capacité de vector ne sera réduite qu'à l'occasion du shrink_to_fit(), le deuxième bloc d'instruction démontre bel et bien que resize() est tout à fait en mesure de réduire la taille du tableau, sans pour autant en modifier la capacité

    (je me souvenais qu'il y avait une astuce, je ne savais plus exactement laquelle )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    OK, je recadre bien le problème :

    On a une fonction qui a doit retourner un vecteur. On ne peut le retourner avec un return en comptant sur le NRVO, parce qu'il y a en fait plusieurs résultats à retourner (là, je n'ai mis que le vecteur).

    Ce vecteur doit donc être vide (en cela, une assertion devrait effectivement être présente). J'ai l'impression que dans ce cas là, tout le monde (moi y compris) préfère le resize(), sans pour autant qu'il n'y ait de vraie raison déterminante.

    Tout cela ne se résumerait-il pas à des préférences de codage ?

  12. #12
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    Pour le RVO, crée une structure/tuple qui contient tous tes résultats.

    Maintenant, dans mes applications (traitement d'images en ce moment), je préfère infiniment le resize(). Au cas où le tableau ne serait pas vide comme Koala et d'autres l'ont dit. Mais je vais aller plus loin. Dans une boucle, oublie le RVO. Surtout si la boucle est appelée des millions de fois si ce n'est plus. resize() est alors ce qu'il y a de mieux. Et la différence est plus que perceptible.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  13. #13
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 392
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le premier bloc d'instructions démontre que, quoi que l'on fasse, la capacité de vector ne sera réduite qu'à l'occasion du shrink_to_fit()
    Mais est-ce la norme, ou est-ce un détail d'implémentation?

    Edit: CppReference ne parle généralement pas de détails d'implémentation, et te donne raison ainsi qu'une raison pourquoi:
    Vector capacity is never reduced when resizing to smaller size because that would invalidate all iterators, rather than only the ones that would be invalidated by the equivalent sequence of pop_back() calls.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Je n'aime pas trop les tuples, mais une fois de plus, c'est très subjectif.

    Pourrais-tu être plus explicite sur ton histoire de boucles et de RVO ?

  15. #15
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    Un problème auquel j'ai été confronté dernièrement: http://wiki.orfeo-toolbox.org/index....mageProcessing

    En "corrigeant" la classe type vecteur pour qu'elle supporte le C++11 la move semantics et le RVO je descends de 120 sec à 95sec sur un traitement qui revient à faire "pixelvectoriel <- p0 + (p1 - p0) * d" dans une boucle, l'expression étant retournée par une fonction. J'ai maintenant une seule allocation par itération. Mais sur 40 milliards d'itérations, c'est beaucoup trop.

    En corrigeant pour ne plus retourner des pixels par valeur, mais par paramètre sortant et faire des resizes, je descends à 20 secondes de temps de traitements.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

Discussions similaires

  1. Réponses: 112
    Dernier message: 22/10/2013, 20h20
  2. Réponses: 1
    Dernier message: 14/05/2012, 18h54
  3. [GNU Emacs] un style de codage java correct
    Par zafo dans le forum Autres Logiciels
    Réponses: 0
    Dernier message: 23/10/2007, 15h05
  4. [POO] Question de style et méthode de codage
    Par elitemedia dans le forum Langage
    Réponses: 1
    Dernier message: 23/12/2006, 22h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo