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 :

Optimisation ou comportement défini par la norme


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut Optimisation ou comportement défini par la norme
    Salut à tous,
    Considérons le code suivant :
    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
    #include <iostream>
     
    using namespace std;
     
    struct A
    {
        A( const A& a ){ cout << "copy ctor" << endl; }
        A( int a ){ cout << "ctor 1 param" << endl; }
    };
     
    int main ()
    {
        A a = A (1);//appel du constructeur à un paramètre puis appel au constructeur par copie???
     
        return 0;
    }
    À l'exécution, je devrais obtenir l'affichage suivant :
    ctor 1 param
    copy ctor


    Or je n'obtiens que ctor 1 param.

    Ma question est : est-ce une optimisation de mon compilateur ou un comportement défini dans la norme?

  2. #2
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par backlash Voir le message
    Salut à tous,
    Considérons le code suivant :
    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
    #include <iostream>
     
    using namespace std;
     
    struct A
    {
        A( const A& a ){ cout << "copy ctor" << endl; }
        A( int a ){ cout << "ctor 1 param" << endl; }
    };
     
    int main ()
    {
        A a = A (1);//appel du constructeur à un paramètre puis appel au constructeur par copie???
     
        return 0;
    }
    À l'exécution, je devrais obtenir l'affichage suivant :
    ctor 1 param
    copy ctor


    Or je n'obtiens que ctor 1 param.

    Ma question est : est-ce une optimisation de mon compilateur ou un comportement défini dans la norme?
    C'est normalisé. L'expression X x = X(args); est équivalente à X x(args); Je recherche la paragraphe de la norme, et je te le donnes (à moins que quelqu'un d'autre ne le fasse avant moi !)

    Edit: je me suis peut-être avancé là. Je n'arrive pas à retrouver ce point de norme, et je trouve des choses qui semblent aller dans le sens contraire (Section 12.6.1 §1).

    Le code suivant :

    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
     
    #include <iostream>
     
    class check
    {
    	int m_i;
     
    private:
    	check(const check& c) : m_i(c.m_i)
    	{ std::cout << "in check::check(const check&)" << std::endl; }
    public:
    	check(int x) : m_i(x)
    	{ std::cout << "in check::check(int)" << std::endl; }
     
    	void out()
    	{ std::cout << "i = " << m_i << std::endl; }
    };
     
    int main()
    {
    	check c = check(20);
            c.out();
    }
    Ne compile pas avec g++ (car le constructeur par copie est privé), ce qui tends à prouver que cette forme d'initialisation nécessite la construction d'un objet puis la copie de cet objet.

    Le mettre en public règle le problème, mais il n'est pas appelé - même si son comportement change du comportement attendu.

    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
     
    #include <iostream>
    #include <unistd.h>
     
    class check
    {
    	int m_i;
     
    public:
    	check(const check& c) : m_i(0) // on met m_i à 0
    	{
    		// attente d'une seconde
    		usleep(1000000);
    		std::cout << "in check::check(const check&)" << std::endl;
    	}
    public:
    	check(int x) : m_i(x)
    	{ std::cout << "in check::check(int)" << std::endl; }
     
    	void out()
    	{ std::cout << "i = " << m_i << std::endl; }
    };
     
    int main()
    {
    	check c = check(20);
    	c.out();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    $~ > make check
    $~ > ./check 
    in check::check(int)
    i = 20
    Si le constructeur par copie est censé être appelé (même dans une version "optimisée"), on devrait avoir i = 0 et une attente d'une seconde. (gcc 4.6)

    La norme n'est hélas pas très claire sur le sujet. 12.6.1 §1 donne un exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    complex c = complex(1,2);
        // construct complex(1,2)
        // using complex(double,double)
        // copy it into c
    La dernière ligne est tout sauf claire : ça veut dire quoi ? qu'on fait une copie brute de l'objet ? possible (ça expliquerait le comportement final). Mais dans ce cas, pourquoi est-ce que le copy-ctor doit être public, puisque ce n'est pas nécessaire et qu'il n'est pas appelé ?

    Il y a là une subtilité de la norme que je ne saisi pas tout à fait.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  3. #3
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    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 296
    Par défaut
    C'est louche. Si le constructeur de copie est explicite ou inhibé, la compilation échoue.
    Pour moi ce n'est qu'une optimisation classique.

    Le §12.1/8 de la norme de 98 fait référence à un temporaire non nommé.
    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...

  4. #4
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    C'est louche. Si le constructeur de copie est explicite ou inhibé, la compilation échoue.
    Pour moi ce n'est qu'une optimisation classique.

    Le §12.1/8 de la norme de 98 fait référence à un temporaire non nommé.
    D'un autre coté, si c'est une optimisation, alors elle est mal implémentée dans gcc, car le comportement devrait être identique au comportement sans l'optimisation (auquel cas, dans mon code, m_i devrait être égal à 0 et la trace devrait s'afficher).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut
    Cela signifierais t-il donc que
    1)même si on déclare explicitement le constructeur par copie (en visibilité public), le compilateur générerait son constructeur par copie par défaut habituelle (pour ce cas particulier) qui servirait à copier le possible temporaire créé?
    2) par contre si la visibilité du constructeur par copie est privée, cela signifie que nous ne voulons pas que les objets instanciés soit copiable, ainsi l'erreur du compilo contribuerait à la sémantique donné à la classe (être non copiable)?
    3)cependant si un temporaire est créé (comme dit en 1) ), il aurait été détruit ce qui amèrait un comportement indéfini à ce que j'ai dit en 1)?, de plus d'après mes tests il n'y a aucune destruction d'objet donc pas de temporaire créé, non?

    Tout cela me fait penser à une optimisation du compilateur.

  6. #6
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    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 296
    Par défaut
    hum ???
    Nous sommes dans l'exacte même situation que "X x = f();" Le RVO & cie peut s'appliquer si le compilo le juge utile -- et que la copie n'est pas interdite.

    Si le constructeur de copie surchargé fait des trucs supplémentaires qui ne correspondent pas à "déplacer" l'intégralité du temporaire non nommé vers x, c'est pas vraiment la faute du compilo. Mais du constructeur qui fait des trucs en plus de son rôle nominal.
    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...

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Ce qui est équivalent (au explicit près), c'est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    X x = arg;
    X x(arg); // Notez le arg, pas args
    Par contre, quand on écrit :
    Un objet temporaire est créé.
    x est créé à partir de cette objet temporaire par le constructeur de copie. Il faut donc que ce constructeur de copie soit accessible (12.7/14).
    Mais ils existe une "optimisation" spéciale autorisée par la langage qui fait que dans ce cas, le compilateur peut simplifier (12.7/15)
    Whenever a temporary class object is copied using a copy constructor, and this object and the copy have the same cvunqualified type, an implementation is permitted to treat the original and the copy as two different ways of referring to the same object and not perform a copy at all, even if the class copy constructor or destructor have side effects.
    J'ai mis des guillemets autour de "optimisation" car une vraie optimisation ne doit pas être détectable, alors que là, ça l'est.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comportement indéfini par la norme ?
    Par Matthieu Brucher dans le forum Langage
    Réponses: 4
    Dernier message: 19/03/2010, 13h29
  2. SQL Server / Fonction définie par l'utilisateur
    Par borgfabr dans le forum Langage SQL
    Réponses: 3
    Dernier message: 08/03/2005, 15h14
  3. Voir quelques enregistrements définis par leur Index
    Par Lucien dans le forum Bases de données
    Réponses: 4
    Dernier message: 06/12/2004, 08h42
  4. Modifier un type défini par l'utilisateur
    Par soazig dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 04/08/2004, 12h47
  5. Fonctions définies par l'utilisateur
    Par DiGueDao dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 25/06/2003, 11h17

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