Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 7 sur 7
  1. #1
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 003
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 003
    Points : 5 169
    Points
    5 169

    Par défaut constructeur et paramètre par référence constante

    Bonjour,

    décidemment, 2 ans de C# et je n'y comprend plus rien au c++

    Prenons le code suivant:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    struct Foo
    {
    	Foo( const std::string & str ) { cout << str << endl; }
    };
     
    void f( const std::string & str )
    {
    	cout << str << endl;
    }
     
    int main()
    {
    	f( "test" ); // ok
    	Foo( "test" ); //ok
    	std::string test= "test";
    	f( test ); //ok
    	Foo( test ); // erreur
    }
    Pourquoi est-ce que la dernière ligne ne compile pas?
    En plus mon compilateur (VS 2010) est complètement perdu, l'erreur qu'il me donne est sans rapport:
    error C2371: 'test' : redefinition; different basic types
    il croit que j'essaie de redéfinir la variable test.

    Sauriez-vous me dire pourquoi cette dernière ligne ne compile pas?

    Il me semblait que le cast en référence constante était automatique.

  2. #2
    Modérateur

    Homme Profil pro Cyrille
    Network programmer
    Inscrit en
    juin 2010
    Messages
    1 893
    Détails du profil
    Informations personnelles :
    Nom : Homme Cyrille
    Âge : 26
    Localisation : France

    Informations professionnelles :
    Activité : Network programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 1 893
    Points : 5 019
    Points
    5 019

    Par défaut

    Bonjour,

    parce qu'il n'existe pas de fonction Foo qui prend un const std::string& en pramètres.

    Foo est un constructeur et la syntaxe est

  3. #3
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 003
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 003
    Points : 5 169
    Points
    5 169

    Par défaut

    Mais pourtant fonctionne

  4. #4
    Membre Expert

    Inscrit en
    mai 2008
    Messages
    1 007
    Détails du profil
    Informations forums :
    Inscription : mai 2008
    Messages : 1 007
    Points : 1 950
    Points
    1 950

    Par défaut

    Il me semble que cette écriture en fait déclare une variable locale "test" de type Foo, exactement comme si l'on avait écrit Foo test; Les parenthèses sont ignorées.
    De la même manière on peut déclarer double (d); au lieu de double d; même si ça ne sert pas à grand chose à part embrouiller le lecteur.

    Ce n'est pas si embêtant qu'il n'y parait vu qu'en pratique on ne va jamais appeler un constructeur directement et créer un objet temporaire mais plutôt écrire :
    Code :
    1
    2
    Foo g(test);
    Foo h = Foo(test);
    Et dans ce cas tout se passe comme prévu.

    Edit :
    Par exemple
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct Fun
    {
       Fun(){std::cout << "ctor\n";}
       Fun(int i){std::cout << "ctor int\n";}
       void toto(){std::cout << "toto\n";}
    };
     
    int main()
    {
       Fun(4); // construit un Fun temporaire avec ctor(int)
       Fun (f); // construit une variable f de type Fun (ctor par défaut);
       f.toto();
    }
    affiche :
    ctor int
    ctor
    toto

    Faut avouer que Les designers du C# en partant de zéro ont eu l'avantage de pouvoir éviter ce genre d’ambiguïté dans la grammaire héritée du C

  5. #5
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 003
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 003
    Points : 5 169
    Points
    5 169

    Par défaut

    Citation Envoyé par Arzar Voir le message
    Ce n'est pas si embêtant qu'il n'y parait vu qu'en pratique on ne va jamais appeler un constructeur directement
    Et bien, ahem... c'est ce que je comptais faire pour mon logger.
    L'idée c'est que j'ai mon logger en singleton (ou variable globale c'est pareil), et je comptais faire une fonction libre dans le style:
    Code :
    void my_log( mon_looger.instance()->Log( "blabla" );
    Sauf qu'en fait, je trouvais pas mal l'idée de, à la place de cette fonction, faire une classe Log qui serait amie de mon logger, afin de tout mettre en privé dans mon logger et ainsi d'en d'interdire totalement l'utilisation.

    Et j'en suis donc arrivé à une classe Log dont le constructeur prend un const string & en paramètre et c'est ce constructeur qui effectue le log. Et ça marchait très bien jusqu'à aujourd'hui où j'ai essayé de logger un objet string (jusqu'ici je ne loggais que des chaines du type "blabla" ).

  6. #6
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 003
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 003
    Points : 5 169
    Points
    5 169

    Par défaut

    A noter également que, en utilisant l'exemple du premier post, le code suivant fonctionne:
    Code :
    1
    2
    string test = "test";
    Foo( test.c_str() );
    et c'est bien le constructeur Foo( const string & ) qui est appelé.

  7. #7
    Expert Confirmé Avatar de Flob90
    Homme Profil pro Florian Blanchet
    Etudiant en Optique
    Inscrit en
    août 2004
    Messages
    1 189
    Détails du profil
    Informations personnelles :
    Nom : Homme Florian Blanchet
    Âge : 23
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Etudiant en Optique

    Informations forums :
    Inscription : août 2004
    Messages : 1 189
    Points : 2 731
    Points
    2 731

    Par défaut

    La syntaxe d'une déclaration c'est :
    Où T est "presque" (*) le type et les Dn des "déclarateurs". Et il se trouve que si un Dn à la forme :
    Alors les parenthèses ne servent "à rien".

    En effet dans certains cas la syntaxe :
    A l'effet que tu attends, mais il ne faut pas oublier que le C++ a une grammaire qui est évaluée en fonction du contexte. Et la grammaire des déclarations semble prioritaire sur celle des expressions (ce qui n'est pas illogique étant donné que la grammaire des expressions régit un fonctionnement "plus fin" que celle des déclarations).

    Ca c'était la partie "grammaire", maintenant il y a quelque chose qui me gène dans le fait que tu voudrais que :
    Code :
    Foo(test); //Tout seul, sans rien autour
    Ai un impact sur le code.

    Le C++ n'est pas un langage pure, mais là c'est quand même un effet de bord un peu fort. Sans compter que les objets temporaires (donc les appels aux constructeurs/destructeurs) sont un point sur lequel les compilateurs optimisent, y introduire des effets de bord nécessaires c'est courir au devant de problèmes incompréhensibles.

    Pour revenir à ton problème, mets les parenthèses autour de Foo et pas de test, tu auras le comportement que tu veux (c'est une notation de cast, mais si ton constructeur n'est pas explicite, ça aura bien le comportement voulu : construire un Foo depuis un string). Sauf si ça optimise encore une fois.

    (*) Dans int * i; T est int et D *i, d'où mon "presque", idem pour la référence et d'autre élément dans le cas d'autre déclaration.
    "We can solve any problem by introducing an extra level of indirection" Butler Lampson

    "N'importe quel problème peut être résolu en introduisant un niveau d'indirection supplémentaire" Butler Lampson (traduction libre)

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •