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 :

[C++11] Range based loop : conserver la position dans le tableau ?


Sujet :

Langage C++

  1. #1
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut [C++11] Range based loop : conserver la position dans le tableau ?
    Bonjour,

    Lorsque l'on utilise la nouvelle syntaxe pour les boucles en C++11, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<double> array(10);
    for (auto data : array)
    {
        // ...
    }
    ... alors dans ce cas, data est de type double. On peut accéder à la valeur, mais une fois dans la boucle, on ne connaît pas a priori sa position dans le std::vector. Une solution est d'itérer sois-même à côté :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    std::vector<double> array(10);
    size_t i = 0;
    for (auto data : array)
    {
        // ...
     
        ++i;
    }
    ... mais on perd l'intérêt de la nouvelle syntaxe.

    Du coup, j'ai implémenté moi-même quelque chose d'assez générique (j'ai condensé un peu le code pour que ça soit lisible sur le forum) :
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    template<class T>
    class pairs_proxy
    {
        T& obj;
     
    public :
     
        pairs_proxy(T& t) : obj(t) {}
     
        typedef typename T::iterator       proxy_iterator;
        typedef typename T::const_iterator proxy_const_iterator;
     
        class iterator
        {
            proxy_iterator iter;
     
            explicit iterator(proxy_iterator i) : iter(i) {}
     
        public :
     
            iterator() = default;
     
            iterator& operator ++ () {
                ++iter; return *this;
            }
     
            iterator operator ++ (int) {
                iterator tmp = *this; return ++tmp;
            }
     
            iterator& operator -- () {
                --iter; return *this;
            }
     
            iterator operator -- (int) {
                iterator tmp = *this; return --tmp;
            }
     
            proxy_iterator& operator * () {
                return iter;
            }
     
            bool operator == (const iterator& i) const {
                return iter == i.iter;
            }
            bool operator != (const iterator& i) const {
                return iter != i.iter;
            }
     
            friend class const_iterator;
            friend class pairs_proxy;
        };
     
        class const_iterator
        {
            proxy_const_iterator iter;
     
            explicit const_iterator(proxy_const_iterator i) : iter(i) {}
     
        public :
     
            const_iterator() = default;
     
            const_iterator(const iterator& i) : iter(i.iter) {}
     
            const_iterator& operator ++ () {
                ++iter; return *this;
            }
     
            const_iterator operator ++ (int) {
                const_iterator tmp = *this; return ++tmp;
            }
     
            const_iterator& operator -- () {
                --iter; return *this;
            }
     
            const_iterator operator -- (int) {
                const_iterator tmp = *this; return --tmp;
            }
     
            proxy_const_iterator& operator * () {
                return iter;
            }
     
            bool operator == (const const_iterator& i) const {
                return iter == i.iter;
            }
            bool operator != (const const_iterator& i) const {
                return iter != i.iter;
            }
     
            friend class pairs_proxy;
        };
     
        iterator begin() {
            return iterator(obj->begin());
        }
     
        iterator end() {
            return iterator(obj->end());
        }
     
        const_iterator begin() const {
            return const_iterator(obj->begin());
        }
     
        const_iterator end() const {
            return const_iterator(obj->end());
        }
    };
     
    template<class T>
    pairs_proxy<T> pairs(T& t) {
        return pairs_proxy<T>(t);
    }
    On l'utilise alors comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<double> array(10);
    for (auto iter : pairs(array))
    {
        // ...
    }
    ... et c'est en réalité équivalent à l'itération "à l'ancienne" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<double> array(10);
    for (auto iter = array.begin(); iter != array.end(); ++iter)
    {
        // ...
    }
    Ma question est alors : existe-t-il déjà quelque chose dans le standard pour faire ce que je souhaite ?
    Et si non, mon implémentation est-elle optimale à votre avis ? Que changeriez-vous ?

    PS : Pour l'anecdote, le mot "pairs" est inspiré de Lua :
    Code Lua : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    local array = {1, 2, 5, 0, -5, 5.2, 85};
    for idx, data in pairs(array) do
        // 'idx' est l'indice sur lequel on itère (automatiquement)
        // 'data' est la valeur de array[idx]
    end
    Si on n'utilise pas pairs dans l'exemple ci-dessus, alors on perd l'information sur idx, et on retrouve une syntaxe très proche du premier morceau de code en haut de ce message :
    Code Lua : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    local array = {1, 2, 5, 0, -5, 5.2, 85};
    for data in array do
        // ...
    end

  2. #2
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    boost::range (un très belle bibliothèque je trouve) :

    http://www.boost.org/doc/libs/1_50_0...e/indexed.html

    Pour l'implémentation, je n'ai pas regarder ton code, mais tu peux essayer de te lancer dans celui de boost::indexed_range pour comparer

  3. #3
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Le côté "on perd la position" est un peu faux, on n'a pas en général la position dans un conteneur, sauf à compter soi-même. Donc pour moi le problème posé n'existe pas avec la nouvelle écriture

  4. #4
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Matthieu Brucher Voir le message
    Le côté "on perd la position" est un peu faux, on n'a pas en général la position dans un conteneur, sauf à compter soi-même. Donc pour moi le problème posé n'existe pas avec la nouvelle écriture
    En interne, les boucles for de ce type utilisent le concept d'itérateurs. Et les itérateurs gardent bien avec eux l'information sur "où trouver la donnée pointée" : ce sont soit de simples pointeurs qui en donnent l'adresse, soit un système plus compliqué mais qui contiendra toujours l'information sur la position de la donnée pointée au sein du conteneur (ce que je raconte est peut être limité aux conteneurs à accès aléatoire).

    Par exemple dans une std::map, l'itérateur est une paire clé/valeur, et la connaissance de la clé identifie sans ambiguïté la valeur. Pour un std::vector, iter - array.begin() te donnera toujours la position de la variable dans le tableau.

    Ce qui me chagrine, c'est qu'on incrémente déjà l'itérateur interne pour parcourir la boucle, mais que l'on a accès qu'à la valeur pointée par cet itérateur. Si on veut récupérer d'autres informations contenues dans l'itérateur (comme la position de la donnée), on doit refaire l'incrémentation sois-même à côté, et c'est du gâchis.

    Le problème se pose donc bien. Par exemple : comment créer un tableau de taille N dont les valeurs sont égales à leur indice dans le tableau en n'utilisant qu'une seule incrémentation par itération et avec la nouvelle syntaxe du C++11 ? Une des réponses consiste à utiliser la classe que j'ai introduite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::vector array(N);
    for (auto iter : pairs(array))
        *iter = iter - array.begin();
    ... et une autre à utiliser boost::indexed (qui fait bel et bien ce que je demande, mais c'est boost, donc pas (encore?) standard ) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::vector array(N);
    for (auto iter : array | boost::adaptors::indexed(0))
        *iter = iter.index();

  5. #5
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Non les ranges (en tant que concept) ne sont pas encore standard, on reste uniquement sur la notion d'itérateur. Mais, personnelement, je ne vais pas me priver d'utiliser un tel outil juste parce que c'est pas dans la bibliothèque standard.

    La notion de "position" dans un conteneur n'existe pas nécessairement, elle n'existe que quand tu lui imposes. En soi un conteneur n'a pas besoin d'être indexé, il a juste besoin d'être parcouru, et c'est cet/ces ordres de parcours qui peuvent définir une indexation.

    Les itérateur ne contiennent pas de "position" (au sens indice), il font juste référence à l'élément. Si tu veux une boucle for range-based avec des références alors utilise auto&.

    Les itérateurs peuvent définir une indexation par rapport à un autre itérateur grâce à la notion de distance entre itérateur, mais pas parcequ'ils contiennent la position, et ainsi cet indexation est propre à la facon dont tes itérateurs parcourent le conteneur. Si ton conteneur est continue en mémoire, alors tu peux faire de même avec auto& et l'arithmétique des pointeurs.

    Quand je regarde la facon dont un for range-based doit se comporter, j'ai bien l'impression qu'il sert à parcourir l'ensemble des données selon un ordre "par défaut" (définit par begin/end) : ie travailler sur un ensemble de données sans se préoccuper de l'ordre. Du coup l'utilisation même d'un for range-based semble être indiqué lorsque l'ordre de parcours n'importe pas : à partir de là, pourquoi te donnerait-il alors une information qui est sensé ne pas être utile ?

    A partir de là soit tu tiens à utiliser cette structure et tu rajoutes donc une indexation à l'ensemble des données (à la manière de boost::range), soit tu utilises un boucle for "classique" avec une pair d'itérateur pour utiliser l'indexation relative qu'ils permettent d'obtenir.

  6. #6
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par =Flob90 Voir le message
    La notion de "position" dans un conteneur n'existe pas nécessairement, elle n'existe que quand tu lui imposes. En soi un conteneur n'a pas besoin d'être indexé, il a juste besoin d'être parcouru, et c'est cet/ces ordres de parcours qui peuvent définir une indexation.
    Exact, et c'est pourquoi j'ai précisé dans mon post précédent que cette discussion n'a probablement de sens que pour des conteneurs à accès aléatoire.

    Citation Envoyé par Flob90 Voir le message
    Les itérateur ne contiennent pas de "position" (au sens indice), il font juste référence à l'élément. Si tu veux une boucle for range-based avec des références alors utilise auto&.
    Je dirais que ça dépend de l'implémentation. Mais le fait est que ton itérateur, tu es sensé pouvoir l'incrémenter : il contient donc l'information nécessaire pour déterminer l'élément suivant. À mon sens, c'est une information de "position" (car on peut définir la "distance" par rapport à l'itérateur begin() comme étant la position, par exemple).

    Citation Envoyé par Flob90 Voir le message
    A partir de là soit tu tiens à utiliser cette structure et tu rajoutes donc une indexation à l'ensemble des données (à la manière de boost::range), soit tu utilises un boucle for "classique" avec une pair d'itérateur pour utiliser l'indexation relative qu'ils permettent d'obtenir.
    Tout à fait. Mais il est clair que la nouvelle syntaxe introduite par le C++11 permet aussi d'avoir un code plus concis, donc moins sujet aux erreurs et plus lisible (cf. l'exemple d'utilisation que je donne de mon code, comparé à la boucle "classique"). C'est en grande partie pour ça qu'elle m'intéresse.
    Si on peut avoir les mêmes fonctionnalités et les mêmes performances (a priori, mais c'est à vérifier) avec deux fois moins de code, pourquoi s'en priver ?

    Citation Envoyé par Flob90 Voir le message
    Mais, personnellement, je ne vais pas me priver d'utiliser un tel outil juste parce que c'est pas dans la bibliothèque standard.
    Bien sûr, moi non plus. Mais boost est tellement pénible à gérer, pour une si petite addition je préfère le faire moi même (surtout quand c'est implémenté en quelques lignes, comme dans le cas présent).

    S'il existe déjà quelque chose de standard qui permette de le faire, alors c'est encore mieux. Mais ça ne semble pas encore être le cas aujourd'hui, donc j'ai la réponse à ma question. Merci à vous

  7. #7
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Citation Envoyé par Kalith Voir le message
    Bien sûr, moi non plus. Mais boost est tellement pénible à gérer, pour une si petite addition je préfère le faire moi même (surtout quand c'est implémenté en quelques lignes, comme dans le cas présent).
    en quoi faire un ./bootstrap.sh && ./b2 install est penible a gerer ?

  8. #8
    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
    Si tu as besoin de la position de l'élément, il ne te faut pas utiliser la nouvelle syntaxe, mais l'ancienne. Ne pas se précipiter sur de nouveaux outils s'ils ne répondent pas à tes besoins.

    Surtout pour les vecteurs, elel 'nest pas si lourde :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    size_t length = vector.size();
    for (size_t i=0; i != length; ++i)

  9. #9
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Joel F Voir le message
    en quoi faire un ./bootstrap.sh && ./b2 install est penible a gerer ?
    Si je me souviens bien, boost utilise son propre système de build (bjam ?). À l'époque la documentation sur le sujet était relativement faible, voir pas à jour (pour être honnête, j'ai dû tomber pendant une transition d'un système de build à un autre). Du coup, je n'ai jamais réussi à installer boost en cross-compilation linux->windows par exemple. Mais ça a probablement dû changer depuis.

    Quoi qu'il en soit, je ne vais certainement pas ajouter une dépendance à boost pour un si petit ajout (j'utilise ce code dans une bibliothèque constituée d'un seul fichier, jusqu'à présent sans aucune dépendance). Si c'est disponible en standard avec le dernier GCC : je l'utilise, et sinon je trouve un moyen pour contourner le problème (faire autrement, ou faire moi-même), et j'attends un éventuel support officiel. Je peux me le permettre, puisque c'est de sucre syntaxique qu'il s'agit : on peut faire sans, même si c'est mieux avec.

    Citation Envoyé par oodini Voir le message
    Si tu as besoin de la position de l'élément, il ne te faut pas utiliser la nouvelle syntaxe, mais l'ancienne. Ne pas se précipiter sur de nouveaux outils s'ils ne répondent pas à tes besoins.
    Au risque de me répéter :
    Citation Envoyé par Kalith
    Mais il est clair que la nouvelle syntaxe introduite par le C++11 permet aussi d'avoir un code plus concis, donc moins sujet aux erreurs et plus lisible (cf. l'exemple d'utilisation que je donne de mon code, comparé à la boucle "classique"). C'est en grande partie pour ça qu'elle m'intéresse.
    Si on peut avoir les mêmes fonctionnalités et les mêmes performances (a priori, mais c'est à vérifier) avec deux fois moins de code, pourquoi s'en priver ?
    Pour être explicite, à choisir entre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<double> array(10);
    for (auto iter : pairs(array))
    {
        // ...
    }
    ... et :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    std::vector<double> array(10);
    for (auto iter = array.begin(); iter != array.end(); ++iter)
    {
        // ...
    }
    ... je choisis la première sans hésitation. Ce nouvel outil ne répond effectivement pas à mes attentes tel quel, mais avec un petit ajout c'est le cas.

  10. #10
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    "Si on peut avoir les mêmes fonctionnalités et les mêmes performances (a priori, mais c'est à vérifier) avec deux fois moins de code, pourquoi s'en priver ?"
    Les performances des deux boucles n'ont pas a être comparées, elle ne font pas exactement la même chose :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for(auto& elt : containor)
    { /*code*/ }
    est équivalent à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    for(auto it = containor.begin(); i != containor.end(); ++it)
    {
      auto& elt = *it;
      /*code*/
    }
    (je simplifie un peu, il y a d'autre cas : tableau, absence de fonction membre begin/end)

    Et écrit comme ca on voit bien que cette boucle n'est utile que si l'on ne s'interesse pas aux itérateurs (ie à l'ordre de parcours) : auto& elt = *it;. Ton code est fonctionnel, mais tout ce qu'il fait c'est introduire un intermédiaire (donc une indirection) pour supprimer une indirection qui est faite par la syntaxe. Au final ca te fait un code avec deux indirections là où aucune n'est nécessaires.

    Ce nouvel outil est déjà un ajout à un ancien outil pour le rendre plus user-friendly dans certains cas d'utilisation. Mais cet ajout utilisé dans les conditions pour lequel il est fait, fait que le code équivalent est identique à celui qu'on aurait écrit sans cet outil. Dans ton cas ton nouvel ajout conserve le coté user-friendly (*), mais le code qu'on aurait écrit sans n'est pas équivalent.

    J'insiste, mais regarde vraiment l'ensemble de la bibliothèque boost::range, tu verras que penser en terme de range est légèrement différent de penser en termes d'itérateurs (et je trouve ca bien plus lisibile personnelement). Pour ce qui est de son installation, elle est header only, il n'y a rien à compiler.

    (*) Ou presque, on se retrouve à manipuler des itérateurs alors qu'avec cette syntaxe on s'attend à des éléments.

  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
    Citation Envoyé par Kalith Voir le message
    Au risque de me répéter :
    C'est justement pour ça que je ne t'ai pas proposé l'alternative avec itérateur, effectivement lourde.

    Une voiture sans moteur, c'est léger et très bien en descente, mais en dehors de cela, il faut peut-être revenir au modèle avec moteur.

  12. #12
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Ton code est fonctionnel, mais tout ce qu'il fait c'est introduire un intermédiaire (donc une indirection) pour supprimer une indirection qui est faite par la syntaxe. Au final ca te fait un code avec deux indirections là où aucune n'est nécessaires.
    Ce n'est pas gratuit oui, j'en suis conscient (*). Et dans le fond, tu as raison : c'est un peu idiot. Mais je trouve dommage qu'on ai pas accès aux itérateurs, alors qu'ils sont bel et bien utilisés par la boucle.

    (*) Ceci dit, GCC 4.7 génère exactement le même code assembleur pour les deux bouts de code de mon message précédent, une fois les options d'optimisation activées (O3, en l’occurrence).

    Citation Envoyé par Flob90 Voir le message
    Ce nouvel outil est déjà un ajout à un ancien outil pour le rendre plus user-friendly dans certains cas d'utilisation. Mais cet ajout utilisé dans les conditions pour lequel il est fait, fait que le code équivalent est identique à celui qu'on aurait écrit sans cet outil. Dans ton cas ton nouvel ajout conserve le coté user-friendly (*), mais le code qu'on aurait écrit sans n'est pas équivalent.

    (*) Ou presque, on se retrouve à manipuler des itérateurs alors qu'avec cette syntaxe on s'attend à des éléments.
    Je suis d'accord sur le fait que cette syntaxe est "prévue pour" être utilisée avec des éléments, et pas des itérateurs. Mais on pouvait dire la même chose d'une boucle for classique le jour où on a introduit les itérateurs : "dans une boucle for, on s'attend à manipuler des indices, pas des itérateurs"... De mon point de vue, la nouvelle syntaxe est un outil de plus pour écrire des boucles, qui attend un objet sur lequel on puisse appeler begin et end (deux fonctions qui retournent un itérateur, que l'on peut incrémenter et dé-référencer). À partir de là, on peut faire ce qu'on veut tant qu'on respecte ces conditions (de la même manière que boost range utilise l'opérateur |, en le détournant de sa sémantique usuelle).

    Citation Envoyé par Flob90 Voir le message
    J'insiste, mais regarde vraiment l'ensemble de la bibliothèque boost::range, tu verras que penser en terme de range est légèrement différent de penser en termes d'itérateurs (et je trouve ca bien plus lisibile personnelement).
    Je pense comprendre l'idée des ranges. Je suis tombé sur ce message sur stackoverflow, qui donne une idée de ce qu'on peut faire avec, et c'est vrai que c'est joli. Au passage, le fait qu'il existe au sein de boost une fonctionnalité similaire à celle que j'essaye tant bien que mal de défendre depuis le début (boost::indexed) montre que ça a une utilité.

    Citation Envoyé par oodini
    C'est justement pour ça que je ne t'ai pas proposé l'alternative avec itérateur, effectivement lourde.
    Mes excuses, je n'avais pas vu ton code (tu as édité ton message entre temps ?). Oui c'est l'alternative la plus sensée, mais il est plus simple de faire une bêtise avec cette notation. Quand on est habitué, j'imagine que le risque que ça se produise est proche de zéro, mais tout débutant se plante un jour soit sur la valeur de départ, soit sur la valeur de fin, soit sur l'itération. Avec la nouvelle notation, c'est impossible.

  13. #13
    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
    Citation Envoyé par Kalith Voir le message
    Mes excuses, je n'avais pas vu ton code (tu as édité ton message entre temps ?).
    Non.

    Citation Envoyé par Kalith Voir le message
    Oui c'est l'alternative la plus sensée, mais il est plus simple de faire une bêtise avec cette notation. Quand on est habitué, j'imagine que le risque que ça se produise est proche de zéro, mais tout débutant se plante un jour soit sur la valeur de départ, soit sur la valeur de fin, soit sur l'itération. Avec la nouvelle notation, c'est impossible.
    C'est également impossible de faire ce que tu veux, alors c'est toi qui vois...

  14. #14
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par Kalith Voir le message
    Ce n'est pas gratuit oui, j'en suis conscient (*). Et dans le fond, tu as raison : c'est un peu idiot. Mais je trouve dommage qu'on ai pas accès aux itérateurs, alors qu'ils sont bel et bien utilisés par la boucle.
    Mais cette boucle est justement faite pour ne pas avoir à manipuler les itérateurs, elle n'a donc aucun raison de donner les outis qu'offrent les itérateurs.

    Citation Envoyé par Kalith Voir le message
    (*) Ceci dit, GCC 4.7 génère exactement le même code assembleur pour les deux bouts de code de mon message précédent, une fois les options d'optimisation activées (O3, en l’occurrence).
    Ca ne vaut pas grand chose comme mesure de performance la comparaison assembleur de deux codes isolés. Ce qui a de la valeur ce sont les benchmarck en situation réel.

    Citation Envoyé par Kalith Voir le message
    Je suis d'accord sur le fait que cette syntaxe est "prévue pour" être utilisée avec des éléments, et pas des itérateurs. Mais on pouvait dire la même chose d'une boucle for classique le jour où on a introduit les itérateurs : "dans une boucle for, on s'attend à manipuler des indices, pas des itérateurs"
    Non, la boucle for ce n'est qu'un boucle while adpaté à un cas spécifique : celui où l'on a une expression d'itération (et pas particulièrement des indices ou même des itérateurs). A contrario d'un boulce for range-based qui est spécialement faite pour traiter l'ensemble des éléments d'un range (et pas de parcourir l'ensemble d'un range selon une itération).

    Citation Envoyé par Kalith Voir le message
    ... De mon point de vue, la nouvelle syntaxe est un outil de plus pour écrire des boucles, qui attend un objet sur lequel on puisse appeler begin et end (deux fonctions qui retournent un itérateur, que l'on peut incrémenter et dé-référencer). À partir de là, on peut faire ce qu'on veut tant qu'on respecte ces conditions (de la même manière que boost range utilise l'opérateur |, en le détournant de sa sémantique usuelle).
    Oui, tu fais ce que tu veux avec ce que te fournit la boucle, et donc un élément (par réfénce ou pas) et pas un itérateur. Là tu cherches à faire quelque chose avec une information que tu n'as pas, et pour ca tu te retrouve à l'introduire en aval alors qu'il a été supprimé volontairement, tu ne trouves pas ca paradoxal ?

    Citation Envoyé par Kalith Voir le message
    Je pense comprendre l'idée des ranges. Je suis tombé sur ce message sur stackoverflow, qui donne une idée de ce qu'on peut faire avec, et c'est vrai que c'est joli. Au passage, le fait qu'il existe au sein de boost une fonctionnalité similaire à celle que j'essaye tant bien que mal de défendre depuis le début (boost::indexed) montre que ça a une utilité.
    Je serais toi je regarderais à nouveau, je pensais que indexed pouvais resoudre ton problème, mais en réalité non.

  15. #15
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    C'est également impossible de faire ce que tu veux, alors c'est toi qui vois...
    Ben si, je l'ai fait, cf. le code dans le premier post. Non ?

    Ca ne vaut pas grand chose comme mesure de performance la comparaison assembleur de deux codes isolés. Ce qui a de la valeur ce sont les benchmarck en situation réel.
    Ça montre quand même que le compilateur est capable de faire l'optimisation. Je ne suis pas expert en benchmark, mais avec quelque chose d'aussi simple que :
    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
    32
    template<typename T>
    void convert(const std::string& s, T& v)
    {
        std::stringstream ss(s);
        ss >> v;
    }
     
    int main(int argc, char** argv)
    {
        size_t T; convert(argv[1], T);
        size_t N; convert(argv[2], N);
     
        std::vector<double> array(N);
     
        auto time = clock();
     
        for (size_t i = 0; i < T; ++i)
        {
            #ifdef STANDARD
            for (auto iter = array.begin(); iter != array.end(); ++iter)
            #else
            for (auto& iter : pairs(array))
            #endif
                *iter = array[0] + (iter - array.begin());
        }
     
        time = clock() - time;
     
        std::cout << "Time : " << time << std::endl;
     
        return 0;
    }
    ... les deux code consomment autant de temps si O3 est activé. Si aucune optimisation n'est effectuée en revanche, on ressent bien une différence qui va de quelques pourcents à près de 40% plus lent dans le pire des cas (T valant 50 millions, et N valant seulement 1).

    Là tu cherches à faire quelque chose avec une information que tu n'as pas, et pour ca tu te retrouve à l'introduire en aval alors qu'il a été supprimé volontairement, tu ne trouves pas ca paradoxal ?
    Elle est là l'information, justement, je cherche juste à la rendre accessible. Et oui c'est paradoxal, on est d'accord. Mais n'est-tu pas d'accord sur le fait qu'on peux faire plus de chose quand on a accès à l'itérateur ? Et que la nouvelle syntaxe est plus simple à utiliser que l'ancienne ?

    Citation Envoyé par Flob90 Voir le message
    Je serais toi je regarderais à nouveau, je pensais que indexed pouvais resoudre ton problème, mais en réalité non.
    Je perds le fil, avec toutes ces boucles... Tu as raison.

  16. #16
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Oui, ca montre que le compilateur est capable d'optimiser dans cette situation, qui est simple. Rien ne garanti qu'il le fera dans un code utilisé en production, d'où le besoin de bencher en situation réel.

    Mais cette nouvelle syntaxe n'est pas là pour remplacer "l'ancienne" (le terme "ancienne" n'est pas approprié, AMA). Elle est là pour être utilisée dans une situation où la syntaxe classique est inutilement verbeuse. La différence entre un for et un for range-based est aussi importante que la différence entre un for et un while.

    Le fonctionnement du for range-based est à rapprocher de std/boost::for_each, son objectif c'est de travailler sur les valeurs d'une séquence, pas sur la séquence en elle-même (donc pas sur les itérateurs).

    D'ailleurs, tu as un besoin spécifique de faire ce que tu veux faire, ou c'est juste une interrogation ?

  17. #17
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Mais cette nouvelle syntaxe n'est pas là pour remplacer "l'ancienne" (le terme "ancienne" n'est pas approprié, AMA). Elle est là pour être utilisée dans une situation où la syntaxe classique est inutilement verbeuse. La différence entre un for et un for range-based est aussi importante que la différence entre un for et un while.
    Oui, j'ai inconsciemment dérapé de "classique" à "ancien", ce qui n'est pas tout à fait la même chose effectivement. Loin de moi l'idée de bannir toute les boucles for classiques de mon code, mais je souhaite profiter des ajouts du C++11 autant que possible.

    Citation Envoyé par Flob90 Voir le message
    Le fonctionnement du for range-based est à rapprocher de std/boost::for_each, son objectif c'est de travailler sur les valeurs d'une séquence, pas sur la séquence en elle-même (donc pas sur les itérateurs).
    Peut-être suis-je biaisé par la macro que j'avais l'habitude d'utiliser avant l'arrivée des range-based for :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define foreach(iter, cont) for (iter = (cont).begin(); iter != (cont).end(); ++(iter))
    Citation Envoyé par Flob90 Voir le message
    D'ailleurs, tu as un besoin spécifique de faire ce que tu veux faire, ou c'est juste une interrogation ?
    "Spécifique" non. Comme je le disais plus haut, c'est juste une commodité : on peut faire autrement de mille autres manières. Mais pour situer le contexte, je travaille sur une bibliothèque d'aide au traitement numérique de problèmes physiques (lien en signature). J'ai des données, stockées dans des conteneurs, et leur position dans le conteneur est une donnée très importante. L'exemple que je donne est celui d'une corde que l'on souhaite étudier numériquement : on la découpe en N points et on applique les équations physiques sur ces points de façon à reproduire approximativement le mouvement de la corde. Ces N points sont stockés dans un tableau, et l'indice auquel ils se trouvent indique leur position sur la corde.

    En gros, j'ai développé une classe qui est un peu comme un gros tableau multidimensionnel, et qui propose des fonctionnalités propres au traitement numérique d'équations mathématiques (dérivation, intégration, etc.). Je veux proposer différentes possibilités pour itérer sur les données contenues dans cette classe, et ai donc introduit un système d'itérateur, avec les fonctions begin(), end() et tout le tintouin. Comme dans boost::indexed, ces itérateurs possèdent une méthode qui permet de récupérer des informations "user-friendly" sur la position de la donnée pointée dans le conteneur.
    Dans la quasi totalité des cas, les itérations se font sur l'ensemble des données (pas de sous ensemble), et je pensais donc qu'il n'étais pas idiot de s'intéresser au range-based for. Mais (et c'est là tout l'objet de la discussion), cette boucle ne me donnait accès qu'à la valeur, et par à l'itérateur (qui contient l'information supplémentaire et essentielle de la position de cette valeur).

  18. #18
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    En effet ta macro doit te donner une vision biaisée, elle te donne un itérateur alors que traditionnelement c'est plus souvent une valeur/référence.

    Sinon je suis pas un spécialiste de la mécanique classique, mais pourquoi tu as besoin de savoir si c'est 3 ou 20 ième points de la corde ? Pour chaque point le traitement est le même (principe fondamentale de la dynamique) il me semble. La seul chose qui introduit des dépendances entre les points c'est les forces exercés par les divers moceaux entre eux (du moins pour la situation à l'équilibre pour la corde) mais on doit pouvoir le faire sans connaitre les positions des différents éléments.

    Edit: Ce n'est pas mon domaine d'étude, mais on n'utilise pas la FFT en mécanique classique moderne ?

  19. #19
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Sinon je suis pas un spécialiste de la mécanique classique, mais pourquoi tu as besoin de savoir si c'est 3 ou 20 ième points de la corde ? Pour chaque point le traitement est le même (principe fondamentale de la dynamique) il me semble. La seul chose qui introduit des dépendances entre les points c'est les forces exercés par les divers moceaux entre eux (du moins pour la situation à l'équilibre pour la corde) mais on doit pouvoir le faire sans connaitre les positions des différents éléments.
    Pas tout à fait. Tu peux avoir des forces qui dépendent de la position, par exemple un vent qui serait plus fort sur la partie gauche de la corde que sur la droite. Autre exemple : si tu étudies le mouvement des électrons dans un solide, tu auras une force électrique qui sera différente selon que tu sois proche d'un atome ou pas.

    Mais le cas le plus fréquent où l'on a potentiellement besoin de connaître la position est pour définir les conditions initiales. Tu peux en effet partir de la corde plate (tous les points sont initialisé à la même valeur), ou une corde tordue, par exemple avec une fonction cos(x) ou sin(x) : dans ce cas tu as besoin d'une position x, reliée linéairement à l'indice i qui identifie une valeur dans le tableau.

    Citation Envoyé par Flob90 Voir le message
    Edit: Ce n'est pas mon domaine d'étude, mais on n'utilise pas la FFT en mécanique classique moderne ?
    C'est une méthode que l'on peut être amené à utiliser dans certains cas, oui, mais pas toujours. Pourquoi ?

  20. #20
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    D'accord, mais tout ca se sont des choses qui dépendent de la position dans le référentiel, tu ne crois pas que chaque point devrait connaitre sa position dans un référentiel ? Du coup ca serait une donnée des objet en eux même et pas une du conteneur.

    En fait utiliser les indices d'un conteneur comme coordonnées spatiales/temporelles lorsque ce qui est caractérisé c'est les points de l'espace/du temps (ie f(r,t), typiquement un champs), mais quand on cherche l'évolution spatial d'un point il me semble plus logique de considérer la position comme la donnée qui évolue (en fonction du temps).

    Et donc dans le cas d'une fonction f(r,t) la FFT est vraiment, AMA, l'outil le plus adapté pour les calculs numérique faisant intervenir des dérivés (temporelles ou spatiales).

    Pour la FFT, je te demandais ca car avec une FFT les dérivés ca va tout seul ^^.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 2
    Dernier message: 27/05/2014, 15h34
  2. lire d'une base de données et stocker dans un tableau
    Par michael_MS dans le forum ASP.NET
    Réponses: 23
    Dernier message: 21/11/2008, 15h00
  3. Position dans un tableau.
    Par dahmane2007 dans le forum VB.NET
    Réponses: 5
    Dernier message: 24/09/2008, 19h29
  4. Détermination d'une position dans un tableau
    Par oLie dans le forum Bibliothèque standard
    Réponses: 13
    Dernier message: 21/05/2008, 10h38
  5. [Base Access] Stocker une Table dans un tableau
    Par La Praline dans le forum VB 6 et antérieur
    Réponses: 78
    Dernier message: 25/04/2007, 15h24

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