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 :

[exercice] construction implicite


Sujet :

Langage C++

  1. #1
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut [exercice] construction implicite
    Voyons voir... celui-ci me parait encore plus tordu que les précédents

    Quelle qu'en soit la raison, nous avons un objet que nous devons pouvoir construire de différente façons. Étant donné que nous sommes en août et que nous avons pas trop envie de réfléchir, nous choisissons de faire un constructeur pour chaque cas. Notre objet comporte une variable membre type qui indique quel constructeur a été utilisé pour créer l'instance. Mais lorsqu'on commence à jouer avec notre objet, ça donne des choses étranges:

    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
    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
     
    struct Object
    {
    	Object(const string & str = "") : type(0) {}
    	Object(const char* str) : type(1) {}
    	Object(int number) : type(2) {}
    	Object(unsigned int number) : type(3) {}
     
    	int type;
    };
     
    int main()
    {
    	vector<int> int_vector = { 1, 2, 3 };
    	vector<Object> object_vector = { int_vector[0] + "a" + int_vector[2], "", int_vector[0] + int_vector.size() + int_vector[2] };
     
    	for (auto it : object_vector)
    		cout << it.type << endl;
     
    	getchar();
    	return 0;
    }
    Questions:
    Quel est l'output de ce programme?
    Pourquoi?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  2. #2
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    ^^

    "L'erreur" est la suivante et vient de char *, plus généralement de T * utilisé comme un tableau.

    Si j'ai le tableau suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char const * const tab = { 'u', 'n', ' ', 't', 'a', 'b' };
    Je peux accéder à la 4ème case de cette façon :
    mais aussi en utilisant l'arithmétique de pointeur :
    Donc ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int_vector[0] + "a" + int_vector[2]
    est équivalent :
    Il s'agit d'une adresse sur la 5ème case du char * "a", c'est une adresse invalide. Son type est toujours char *.

    ---

    Dans cette lignée voici quelque chose "d'amusant" :
    Comme tab[3]; // 't' est équivalent à *(tab + 3); // 't'. C'est aussi équivalent à :
    Et le code suivant est également équivalent car l'opérateur [] est aussi commutatif :
    ---

    Il y a (ou peut avoir) une erreur de compilation dans le code car int_vector[0] + int_vector.size() + int_vector[2] retourne un std::vector<int>::size_type (souvent std::size_t) qui est souvent (?) un long unsigned int.
    Ce dernier ne peut pas être converti en Object car pour cela il faudrait un constructeur de Object qui prenne directement le bon type (conversion implicite par constructeur avec un seul argument). Or ici il doit faire deux conversions implicites (dont une dangereuse car risque d'overflow) (de std::vector<int>::size_type vers unsigned int, puis de vers Object).

  3. #3
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    Il y a (ou peut avoir) une erreur de compilation dans le code car int_vector[0] + int_vector.size() + int_vector[2]
    gcc ne compile pas ce code?
    vs 2013 oui, mais avec un warning. Je trouvais le coup du size_t -> unsigned int assez sournois, parce qu'en général, on ne fait pas trop attention au fait que la fonction size() retourne un size_t, et non pas un simple int.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  4. #4
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par r0d Voir le message
    vs 2013 oui, mais avec un warning. Je trouvais le coup du size_t -> unsigned int assez sournois, parce qu'en général, on ne fait pas trop attention au fait que la fonction size() retourne un size_t, et non pas un simple int.
    std::size_t est un entier non signé suffisamment grand pour parcourir la mémoire, ça ne pourra jamais être un int. Donc il reste unsigned char (?), unsigned int, long unsigned int et long long unsigned int.
    Mais std::vector<T>::size_type n'est pas forcément un std::size_t, mais il doit être non signé lui-aussi.

    Citation Envoyé par r0d Voir le message
    gcc ne compile pas ce code?
    Voici tous les soucis avec GCC 4.9.1 et cette commande pour la compilation (erreur à la ligne 20) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g++ -Wall -Wextra -Wconversion -Wsign-conversion -std=c++11 -pedantic main.cpp -o main
    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
    main.cpp:11:30: warning: unused parameter ‘str’ [-Wunused-parameter]
      Object(const string & str = "") : type(0) {}
                                  ^
    main.cpp:12:21: warning: unused parameter ‘str’ [-Wunused-parameter]
      Object(const char* str) : type(1) {}
                         ^
    main.cpp:13:13: warning: unused parameter ‘number’ [-Wunused-parameter]
      Object(int number) : type(2) {}
                 ^
    main.cpp:14:22: warning: unused parameter ‘number’ [-Wunused-parameter]
      Object(unsigned int number) : type(3) {}
                          ^
    main.cpp: In function ‘int main()’:
    main.cpp:22:108: warning: conversion to ‘std::vector<int>::size_type {aka long unsigned int}’ from ‘__gnu_cxx::__alloc_traits<std::allocator<int> >::value_type {aka int}’ may change the sign of the result [-Wsign-conversion]
      vector<Object> object_vector = { int_vector[0] + "a" + int_vector[2], "", int_vector[0] + int_vector.size() + int_vector[2] };
                                                                                                                ^
    main.cpp:22:124: warning: conversion to ‘std::vector<int>::size_type {aka long unsigned int}’ from ‘__gnu_cxx::__alloc_traits<std::allocator<int> >::value_type {aka int}’ may change the sign of the result [-Wsign-conversion]
      vector<Object> object_vector = { int_vector[0] + "a" + int_vector[2], "", int_vector[0] + int_vector.size() + int_vector[2] };
                                                                                                                                ^
    main.cpp:22:126: error: conversion from ‘std::vector<int>::size_type {aka long unsigned int}’ to ‘Object’ is ambiguous
      vector<Object> object_vector = { int_vector[0] + "a" + int_vector[2], "", int_vector[0] + int_vector.size() + int_vector[2] };
                                                                                                                                  ^
    main.cpp:22:110: note: candidates are:
      vector<Object> object_vector = { int_vector[0] + "a" + int_vector[2], "", int_vector[0] + int_vector.size() + int_vector[2] };
                                                                                                                  ^
    main.cpp:14:2: note: Object::Object(unsigned int)
      Object(unsigned int number) : type(3) {}
      ^
    main.cpp:13:2: note: Object::Object(int)
      Object(int number) : type(2) {}
      ^
    In file included from /usr/include/c++/4.9/vector:64:0,
                     from main.cpp:6:
    /usr/include/c++/4.9/bits/stl_vector.h:373:7: note: initializing argument 1 of ‘std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = Object; _Alloc = std::allocator<Object>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<Object>]’
           vector(initializer_list<value_type> __l,
           ^

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Pour le coup, je me demande où tu trouves l'idée de ces exercices.
    Si tu as une liste des sournoiseries du C++, je suis preneur!
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Je les trouve dans le logiciel sur lequel je travaille actuellement. C'est de la maintenance, le code n'est pas de moi, et je peux vous dire que c'est gratiné -.-
    Par exemple, il y a un système de sérialisation, qui comporte cinq couches d'indirection, en passant par des templates, de l'inplace_factory au milieu, puis qui revient sur de la surcharge de fonction (2ème exercice que j'ai posté).
    Genre le tout premier exercice que j'ai posté (auto à la place de auto &), j'ai mis 2 jours à trouver le bug. Et c'est quand je l'ai trouvé que ça m'a donné l'idée de faire ces petits exercices.
    Le mec qui a codé ça s'est fait plaisir, il a utilisé toutes les fonctionnalités de c++11 même s'il n'y en avait pas besoin, quitte à ajouter des couche d'abstraction. Maintenant je suis sur un bug qui, à mon avis, est un problème de move constructor qui est mal implémenté. C'est pas simple parce que c'est une classe template avec Trait et Policy (façon STL), mais aussi avec des buffers "C-style" (donc malloc, realloc, etc.). Et j'ai pas le temps de refactorer, je dois juste debugger à la rache. Nul doute que je trouverai d'autres petites perles dans les semaines à venir.

    edit: j'ai sous les yeux le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // code
    if (!found)
    {
    	throw(std::exception("Fuck life!"));
    }
    // code
    S'pa gagné :/
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  7. #7
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par r0d Voir le message
    gcc ne compile pas ce code?
    vs 2013 oui, mais avec un warning. Je trouvais le coup du size_t -> unsigned int assez sournois, parce qu'en général, on ne fait pas trop attention au fait que la fonction size() retourne un size_t, et non pas un simple int.
    En fait, j’ai pris l’habitude de compiler en -Wall -Wextra, et gcc est impitoyable sur cet avertissement.

    C’est d’ailleurs l’un des rares endroits où j’ai des casts « à la C » qui traînent dans mon code : pouvoir comparer le retour de size() avec un int que j’ai récupéré d’ailleurs.

Discussions similaires

  1. [Cycle de Vie] exercice sure construction de BDD et sys d'info
    Par vayper22 dans le forum Merise
    Réponses: 1
    Dernier message: 25/11/2014, 18h51
  2. exercice sure construction de BDD et sys d'info
    Par vayper22 dans le forum Programmation système
    Réponses: 0
    Dernier message: 23/11/2014, 13h32
  3. exercice sure construction de BDD et sys d'info
    Par vayper22 dans le forum ALM
    Réponses: 0
    Dernier message: 23/11/2014, 13h21
  4. construction implicite de classe
    Par gorgonite dans le forum F#
    Réponses: 22
    Dernier message: 13/05/2008, 23h12
  5. Pouvez vous m'aider a resoudres ces 3 exercices
    Par algorithmique dans le forum Algorithmes et structures de données
    Réponses: 11
    Dernier message: 09/08/2002, 17h26

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