Considérant ce fragment de code:
struct My { operator const char*()const{ return "my"; } } my; CStringA s( "aha" ); printf("%s %s", s, my ); // another variadic function to get rid of comments about printf :) void foo( int i, ... ) { va_list vars; va_start(vars, i); for( const char* p = va_arg(vars,const char*) ; p != NULL ; p=va_arg(vars,const char*) ) { std::cout << p << std::endl; } va_end(vars); } foo( 1, s, my );
cstringa * code> interprété comme un const char * code> li >
- Si l'appel de la fonction variadique appelle
opérateur (const caractère *) code> dessus, pourquoi ne le feriez-il pas pour ma propre classe? Li>
ul> Quelqu'un peut-il expliquer cela? P>
Edit: Ajout d'une fonction variable factice qui traite ses arguments comme const char * code> s. Voici - il se bloque même lorsqu'il atteint le mon code> argument ... p> p>
7 Réponses :
Ce que vous faites est un comportement indéfini et est une extension non standard fournie par votre compilateur ou vos œuvres de Porte-Chances. Je suppose que la cString stocke les données de chaîne comme premier élément de la structure, et donc que la lecture de la cstring code> comme s'il s'agissait d'un
char * code> donne une validité valide Chaîne null terminée. p>
Il ne passe pas un cstring * code> mais un
cstring code>, qui surcharge
opérateur (lpctstr) code>.
Très bien, j'ai raté ça. Dans ce cas, c'est une extension non standard.
Pourquoi est-ce une extension non standard?
S'il n'est pas défini par la norme C ++, alors par définition, il s'agit d'une extension non standard. Je suppose que la norme C ++ ne le définissait pas car les fonctions variadiques étaient considérées comme une maintien de C qui ont été laissées pour la compatibilité en arrière et la surcharge de l'opérateur (comme dans std :: cout code>) a été considéré comme une meilleure façon de faire la même chose. Mais c'est juste une supposition.
En effet: après une attention particulière, il semble que le premier membre de la classe de modèle code> csimplestringt code> est pxstr m_pszdata code>. Donc, réinterpréter un
cstringt code> comme
const char * code> est, par pure chance, d'accord. Merci!
Édité pour être correct sur le type étant passé de la valeur, pas d'adresse. Ce comportement ressemblant à des réinterprétes, même si techniquement ub, nécessiterait également la taille de (Cstringa) == Tailleof (Char *), sinon VA_ARG appliquera un décalage incorrect pour d'autres arguments.
Si l'appel de la fonction variadique appelle son opérateur (Cons-Char *) dessus, pourquoi ne le feriez-vous pas pour ma propre classe? P> blockQuote>
Oui, mais vous devriez le jeter explicitement dans votre code:
printf ("% s", (lpcstr) s, ...); code>. p>.
Votre instruction PrintF est erronée: devrait être: p> qui imprimera "Aha mon". P > cstring a un opérateur de convertission pour L'appel code> impression code> ne convertira pas votre C'est une bonne pratique (et requis dans de nombreux cas) pour lancer explicitement vos objets / montants sur ce que vous voulez qu'ils soient lorsque vous appelez const char * code> (c'est en fait pour
lpctstr code> qui est un
const tchar * code> -
cstringa code> a une fonction de conversion pour
lpcstr code>). p>
cstringa code> objet à A
cstringa * code> pointeur. Il le traite essentiellement comme un
Void * code>. Dans le cas de CSSTRING, c'est une chance (ou peut-être la conception de développeurs de Microsoft en tirant parti de quelque chose qui n'est pas dans la norme) qu'il vous donnera la chaîne de résiliation null. Si vous deviez utiliser un
_bstr_t code> à la place (qui a la taille de la chaîne en premier), malgré la fonction de conversion, cela échouerait horriblement. P>
printf code> (ou toute fonction variable à ce sujet). p> p>
Il utilise explicitement cstringa code> de sorte qu'il veut
(lpcstr) code>.
En effet. J'ai ajouté le mon code> plus tard pour illustrer le point. Je vais éditer la question donc comme cela.
Comment ça va "le jeter à quelque chose qui est utile"? Où est-ce permis?
Printf code> ne sera pas lancé. Il sera
Reterpret_cast Code>. Il ne connaît pas le type de l'argument, donc ne peut pas utiliser l'opérateur de distribution défini dans
cstringt code>.
@Fred: Vous êtes correct. Je vais résoudre mon libellé, mais le principe de base derrière tout cela est que si vous spécifiez ce que vous voulez, il est passé comme dans l'appel de la fonction, c'est un comportement indéfini.
Vous ne pouvez pas insérer de données non-POD dans des fonctions variadiques. Plus d'infos P>
Vous pouvez, mais c'est un comportement indéfini.
Vous pouvez passer mon, mais pas cstring. Mon est une pod et est donc transmis sans couverture. La fonction appelée n'a aucune idée que c'est mon et ce qu'il devrait faire avec elle.
@shacharptooth: "Vous ne pouvez pas ..." est couramment utilisé pour "Ce serait UB si vous ...", telles que "Vous ne pouvez pas indexer un tableau avec une valeur hors limite".
@Fred Nurk: Oui, mais les gens font quelque chose "tu ne peux pas faire", puis nous plaignez-vous que leur compilateur / exécution n'a pas éventuellement émettre une erreur.
@shacharpôt: Ils méritent de ce qu'ils obtiennent.
Si l'appel de fonction variadique est traduit en poussant les pointeurs des arguments, ... p> blockQuote>
Ce n'est pas la façon dont les fonctions variadiques fonctionnent. Les em> les valeurs em> des arguments, plutôt que des indications des arguments, sont transmises, après des règles de conversion spéciales pour des types intégrés (tels que le caractère de l'int). P>
C ++ 03 §5.2.2P7: P>
Lorsqu'il n'y a pas de paramètre pour un argument donné, l'argument est transmis de manière à ce que la fonction de réception puisse obtenir la valeur de l'argument en invoquant VA_ARG (18.7). Les conversions standard des conversions standard (4.2) et fonction-à-pointeur (4.2) sont effectuées sur l'expression de l'argument. Après ces conversions, si l'argument n'a pas d'arithmétique, d'énumération, de pointeur, de pointeur à un membre ou de type de classe, le programme est mal formé. Si l'argument a un type de classe non pod (clause 9), le comportement n'est pas défini. Si l'argument a un type d'intégration ou d'énumération soumis aux promotions intégrales (4.5) ou à un type de point flottant soumis à la promotion du point flottant (4.6), la valeur de l'argument est convertie au type promu avant l'appel. . Ces promotions sont appelées promotions d'argument par défaut. P> blockQuote>
en particulier de ce qui précède: p>
Si l'argument a un type de classe non pod (clause 9), le comportement est indéfini. P> blockQuote>
C ++ Punmis à C pour la définition de VA_ARG, et C99 TC3 §7.15.1.1.2P2 dit: P>
... Si le type n'est pas compatible avec le type de l'argument suivant réel (tel que promu en fonction des promotions d'argument par défaut), le comportement est indéfini, à l'exception des cas suivants: [Liste des cas qui ne s'appliquent pas ici] p> blockQuote>
Ainsi, si vous passez un type de classe, il doit s'agir de la POD et la fonction de réception doit appliquer le type correct, sinon le comportement n'est pas défini. Cela signifie que dans le pire des cas, cela peut fonctionner exactement comme vous vous attendez. P>
Printf n'appliquera pas le type correct de tout type de classe défini par l'utilisateur, car il n'a aucune connaissance, de sorte que vous ne pouvez pas transmettre aucun type de classe UDT à imprimerf. Votre FOO fait la même chose en utilisant un pointeur de caractère au lieu du type de classe correct. P>
«Dans le pire des cas, cela fonctionne exactement comme vous vous attendez» ... J'aime ça!
Le texte pertinent de C ++ 98 standard §5.2.2 / 7: p>
Le lvalue-to-rvalue (4.1), les conversions standard (4.2) et fonction-à-pointer (4.3) sont effectuées sur l'expression de l'argument. Après ces conversions, si l'argument n'a pas d'arithmétique, d'énumération, de pointeur, de pointeur à un membre ou de type de classe, le programme est mal formé. Si l'argument a un type de classe non pod (clause 9), le comportement est indéfini. P> blockQuote>
Donc officiellement, le comportement est indéfini fort>. p>
Cependant, un compilateur donné peut fournir n'importe quel nombre d'extensions de langues et Visual C ++ fait. Le Library MSDN documente le comportement de Visual C ++ comme suit, avec respect Transmettre des arguments à
... CODE>: P>
- Si l'argument réel est de type float, il est favorisé de taper le double avant l'appel de la fonction. Li>
- Tout type de caractère signé ou non signé, de type chéri ou énuméré ou de champ de bit est converti en un Int signé ou non signé à l'aide de la promotion intégrale. LI>
- Tout argument de type de classe est passé par la valeur en tant que structure de données; La copie est créée par copie binaire au lieu d'invoquer le constructeur de copie de la classe (si l'on existe). Li> ul> blockQuote>
Cela ne mentionne rien à propos de Visual C ++ appliquant des conversions définies par l'utilisateur. p>
ms
cstring code> est "intelligemment" porté, de sorte que la représentation de la pod est exactement le pointeur de sa chaîne de caractères NULL terminée. (
Tailleof (CStringa) == Tailleof (Char *) CODE>) Lorsqu'il est utilisé dans tout em> la fonction de style printf, la fonction vient de recevoir le pointeur de caractères. P>
Cela fonctionne donc en raison du dernier point ci-dessus et de la manière dont
cstring code> est mise hors tension. P>
Ce n'est pas le cas. Cela n'appelle même pas l'opérateur Const de cont * code>. Visual C ++ passe juste les données de la classe à
Printf code> comme si par
memcpy code>. Cela fonctionne en raison de la disposition de la classe code> cstring COD>, il ne contient qu'une variable d'un membre qui est un pointeur sur les données de caractères. P>
@David Heffeman: Je sais.
printf code> est ma fonction variadic prototype. Le code réel que je me réfère est un
cstring :: format code>.
Quelle plate-forme est-ce? Visual Studio n'effectue aucune conversion, il appuie simplement toute la valeur sur la pile. GCC émet une affirmation. Je suppose que vous êtes juste "chanceux", le premier membre de CSstring est un pointeur sur les données.
@Suma: Voulez-vous mettre parier le premier membre de CSTString est un pointeur sur les données exprès i>?
Il peut être exprès, mais à des fins exprès de permettre la transmission de fonctions variadiques, comme cela ne fonctionne de toute façon pas - vous pouvez passer un CString de cette façon, mais pas deux.