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 :

Pourquoi ne peut-on pas modifier une rvalue non objet en C++ ?


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut Pourquoi ne peut-on pas modifier une rvalue non objet en C++ ?
    Pourquoi ne peut-on pas modifier une rvalue non objet alors que l'on peut appeler une fonction membre non constante sur une rvalue objet non constante ?
    Notamment, pourquoi n'a-t-on pas le droit de faire une pré-incrémentation sur un pointeur nu qui est une rvalue alors qu'on a le droit d'appeler operator++ sur une rvalue objet non constante ?

    Exemple avec g++ 6.2.0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <set>
    #include <array>
     
    int main() {
        std::set<int>      set = {0, 1, 2, 3};
        std::array<int, 4> tab = {0, 1, 2, 3};
        auto setIt = ++set.begin(); // Compile, car std::set<int>::iterator est un objet.
        auto tabIt = ++tab.begin(); // Ne compile pas, car std::array<int, 4>::iterator est un pointeur nu.
        return 0;
    }
    A quoi sert cette différence de règle ?

    Je sais que le code générique pour récupérer un itérateur vers le deuxième élément d'un conteneur de la STL est std::next(conteneur.begin()).
    Mais je trouve dommage qu'il faille passer par un modèle de fonction std::next pour contourner le fait que ++conteneur.begin() ne compile pas avec certains conteneurs. Je pense que cela complique inutilement l'apprentissage de la programmation générique en C++.

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Je poserais plutôt la question inverse :
    Pourquoi peut-on appeler une fonction membre non constante sur une rvalue objet non constante alors que l'on ne peut normalement pas modifier une rvalue ?

    Modifier une rvalue est une source d'erreur, la ligne ++set.begin(); compile et elle pourrait tromper un débutant.

    Une fonction ou un opérateur peut produire une rvalue, mais ne devrait pas permettre d'en modifier une. Les opérateurs des conteneurs devraient être : T& operator++() & et T const& operator++()const & pour n'accepter que les lvalues.

  3. #3
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Pourquoi peut-on appeler une fonction membre non constante sur une rvalue objet non constante alors que l'on ne peut normalement pas modifier une rvalue ?
    Cela permet :


    Du coup, je viens de répondre à ma propre question, étant donné que, pour les non-objets :
    • la copie est peu coûteuse et sera probablement optimisée par le compilateur et
    • le named parameter idiom ne s'applique qu'aux objets.


    Mais je trouve toujours dommage que cela complique l'apprentissage de la programmation générique.

  4. #4
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    auto setIt = ++set.begin(); // Compile, car std::set<int>::iterator est un objet.
    Attention, ce n'est pas parce que std::set<int>::iterator est un objet que ce code compile, mais parce que l'implémentation de l'opérateur ++ a été réalisé ici en tant que fonction membre.

    S'il avait été défini en tant que fonction libre sous la forme iterator & operator++(iterator &), ce code ne compilerait pas non plus (sauf à définir une surcharge acceptant iterator &&).

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    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 395
    Par défaut
    Donc en gros, on ne peut pas passer un temporaire non-nommé à une fonction attendant une référence non-const, mais on peut appeler une fonction membre non-const sur un temporaire non-nommé?
    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.

  6. #6
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    @Médinoc : Ça dépend quelle fonction membre non-const :
    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
    struct A {
        void foo   ()          {}
        void fooLV () &        {}
        void fooRV () &&       {}
        void fooC  () const    {}
        void fooCLV() const &  {}
        void fooCRV() const && {}
    };
     
    void barLV (A&        a) {}
    void barRV (A&&       a) {}
    void barCLV(const A&  a) {}
    void barCRV(const A&& a) {}
     
    int main() {
        A{}.foo   ();
    //  A{}.fooLV (); // Ne compile pas.
        A{}.fooRV ();
        A{}.fooC  ();
        A{}.fooCLV();
        A{}.fooCRV();
    //  barLV (A{}); // Ne compile pas.
        barRV (A{});
        barCLV(A{});
        barCRV(A{});
        return 0;
    }
    Citation Envoyé par dalfab Voir le message
    Les opérateurs des conteneurs devraient être : T& operator++() & et T const& operator++()const & pour n'accepter que les lvalues.
    T const& operator++()const & accepte les rvalues à cause du const.
    Mais une telle fonction n'a pas de raison d'être const, car elle sert habituellement à modifier l'objet.
    Même std::back_insert_iterator::operator++ n'a pas de surcharge const alors qu'il ne fait rien.

Discussions similaires

  1. Réponses: 11
    Dernier message: 15/09/2008, 10h25
  2. Réponses: 25
    Dernier message: 02/08/2008, 17h11
  3. Pourquoi ne peut-on pas instancier de tableau paramétré ?
    Par LGnord dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 28/03/2008, 08h59
  4. ne pas modifier une zone de texte
    Par gailup dans le forum Langage
    Réponses: 3
    Dernier message: 22/06/2006, 19h28

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