Précédent   Forum du club des développeurs et IT Pro > C et C++ > C++ > Langage
Langage Langage C++, Programmation Orientée Objet, Templates, etc. Avant de poster : FAQ C++
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 28/12/2012, 14h51   #1
r0d
Expert Confirmé Sénior
 
Inscription : août 2004
Messages : 3 665
Détails du profil
Informations personnelles :
Localisation : Belgique

Informations forums :
Inscription : août 2004
Messages : 3 665
Points : 4 421
Points : 4 421
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:
Citation:
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.
r0d est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 14h58   #2
Bousk
Modérateur
 
Homme Cyrille
Network programmer
Inscription : juin 2010
Messages : 1 546
Détails du profil
Informations personnelles :
Nom : Homme Cyrille
Âge : 25
Localisation : France

Informations professionnelles :
Activité : Network programmer

Informations forums :
Inscription : juin 2010
Messages : 1 546
Points : 4 085
Points : 4 085
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
Bousk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 15h06   #3
r0d
Expert Confirmé Sénior
 
Inscription : août 2004
Messages : 3 665
Détails du profil
Informations personnelles :
Localisation : Belgique

Informations forums :
Inscription : août 2004
Messages : 3 665
Points : 4 421
Points : 4 421
Mais pourtant fonctionne
r0d est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 15h09   #4
Arzar
Membre Expert
 
Inscription : mai 2008
Messages : 937
Détails du profil
Informations forums :
Inscription : mai 2008
Messages : 937
Points : 1 783
Points : 1 783
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
Arzar est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 15h29   #5
r0d
Expert Confirmé Sénior
 
Inscription : août 2004
Messages : 3 665
Détails du profil
Informations personnelles :
Localisation : Belgique

Informations forums :
Inscription : août 2004
Messages : 3 665
Points : 4 421
Points : 4 421
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" ).
r0d est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 15h34   #6
r0d
Expert Confirmé Sénior
 
Inscription : août 2004
Messages : 3 665
Détails du profil
Informations personnelles :
Localisation : Belgique

Informations forums :
Inscription : août 2004
Messages : 3 665
Points : 4 421
Points : 4 421
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é.
r0d est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/12/2012, 18h56   #7
Flob90
Modérateur
 
Avatar de Flob90
 
Homme Florian Blanchet
Etudiant en Optique
Inscription : août 2004
Messages : 1 060
Détails du profil
Informations personnelles :
Nom : Homme Florian Blanchet
Âge : 22
Localisation : France, Essonne (Île de France)

Informations professionnelles :
Activité : Etudiant en Optique

Informations forums :
Inscription : août 2004
Messages : 1 060
Points : 2 492
Points : 2 492
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)
Flob90 est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 13h42.


 
 
 
 
Partenaires

Hébergement Web