La sortie de ce programme:
#include <iostream>
class c1
{
public:
c1& meth1(int* ar) {
std::cout << "method 1" << std::endl;
*ar = 1;
return *this;
}
void meth2(int ar)
{
std::cout << "method 2:"<< ar << std::endl;
}
};
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu).meth2(nu);
}
Est:
method 1
method 2:0
Pourquoi nu
pas 1 quand meth2()
commence?
c++
chaining
operator-precedence
Moises Viñas
la source
la source
nu
,&nu
etc
sur la pile dans cet ordre, puis Invokemeth1
, pousser le résultat sur la pile, puis Invokemeth2
, alors qu'une convention d' appel fondé sur les registres voudrait chargerc
et&nu
dans les registres, invoquermeth1
, chargernu
dans un registre, puis invoquermeth2
.Réponses:
Parce que l'ordre d'évaluation n'est pas spécifié.
Vous voyez
nu
enmain
étant évalué0
avant même d'meth1
être appelé. C'est le problème du chaînage. Je conseille de ne pas le faire.Créez simplement un programme agréable, simple, clair, facile à lire et à comprendre:
la source
<<
pour la sortie, et les "constructeurs d'objets" pour les objets complexes avec trop d'arguments pour les constructeurs - mais cela se mélange vraiment mal avec les arguments de sortie.meth1
etmeth2
est défini, mais l'évaluation du paramètre pourmeth2
peut se produire avantmeth1
est appelée ...?meth2(meth1(c, &nu), nu)
Je pense que cette partie du projet de norme concernant l'ordre d'évaluation est pertinente:
et aussi:
Donc, pour votre ligne
c.meth1(&nu).meth2(nu);
, considérez ce qui se passe dans l'opérateur en termes d'opérateur d'appel de fonction pour l'appel final àmeth2
, afin que nous voyions clairement la ventilation dans l'expression et l'argument postfixnu
:Les évaluations de l'expression et de l'argument postfix pour l'appel de fonction final (c'est-à-dire l'expression postfixe
c.meth1(&nu).meth2
etnu
) ne sont pas séquencées les unes par rapport aux autres selon la règle d' appel de fonction ci-dessus. Par conséquent, l' effet secondaire du calcul de l'expression de suffixe sur l'objet scalairear
n'est pas séquencé par rapport à l'évaluation de l'argumentnu
avant l'meth2
appel de fonction. D'après la règle d' exécution du programme ci-dessus, il s'agit d'un comportement non défini.En d'autres termes, il n'est pas nécessaire que le compilateur évalue l'
nu
argument de l'meth2
appel après l'meth1
appel - il est libre de supposer qu'aucun effet secondairemeth1
n'affecte l'nu
évaluation.Le code d'assemblage produit par ce qui précède contient la séquence suivante dans la
main
fonction:nu
est allouée sur la pile et initialisée avec 0.ebx
dans mon cas) reçoit une copie de la valeur denu
nu
etc
sont chargées dans les registres de paramètresmeth1
est appelénu
dans leebx
registre sont chargés dans les registres de paramètresmeth2
est appeléDe manière critique, à l'étape 5 ci-dessus, le compilateur permet à la valeur mise en cache
nu
de l'étape 2 d'être réutilisée dans l'appel de fonction àmeth2
. Ici, il ne tient pas compte de la possibilité quinu
peut avoir été modifiée par l'appel àmeth1
«comportement indéfini» en action.REMARQUE: Cette réponse a changé en substance par rapport à sa forme originale. Mon explication initiale en termes d'effets secondaires du calcul des opérandes n'étant pas séquencée avant l'appel de fonction final était incorrecte, car ils le sont. Le problème est le fait que le calcul des opérandes eux-mêmes est séquencé de manière indéterminée.
la source
meth1
est exécuté avantmeth2
, mais le paramètre pourmeth2
est une valeur denu
mis en cache dans un registre avant l'appel àmeth1
- c'est- à -dire que le compilateur a ignoré les effets secondaires potentiels, ce qui est conforme à ma réponse.c.meth1(&nu).meth2
) et l'évaluation de l'argument de cet appel (nu
) sont généralement non séquencées, mais 1) leurs effets secondaires sont tous séquencés avant l'entrée dansmeth2
et 2) puisqu'ilc.meth1(&nu)
s'agit d'un appel de fonction , il est séquencé de manière indéterminée avec l'évaluation denu
. À l'intérieurmeth2
, s'il obtenait en quelque sorte un pointeur vers la variable dansmain
, il verrait toujours 1.meth2
, comme indiqué au point 3 de la page de référence que vous citez (que vous avez également négligé de citer correctement).Dans la norme C ++ de 1998, section 5, paragraphe 4
(J'ai omis une référence à la note de bas de page # 53 qui n'est pas pertinente pour cette question).
Essentiellement,
&nu
doit être évalué avant l'appelc1::meth1()
etnu
doit être évalué avant l'appelc1::meth2()
. Il n'y a, cependant, aucune exigencenu
à évaluer avant&nu
(par exemple, il est permis d'nu
être évalué en premier, puis&nu
, puisc1::meth1()
appelé - ce qui pourrait être ce que fait votre compilateur). Il n'est donc pas garanti que l' expression*ar = 1
inc1::meth1()
soit évaluée avant quenu
inmain()
soit évaluée, afin d'être transmise àc1::meth2()
.Les standards C ++ ultérieurs (que je n'ai pas actuellement sur le PC que j'utilise ce soir) ont essentiellement la même clause.
la source
Je pense que lors de la compilation, avant que les fonctions meth1 et meth2 ne soient vraiment appelées, les paramètres leur ont été passés. Je veux dire quand vous utilisez "c.meth1 (& nu) .meth2 (nu);" la valeur nu = 0 a été passée à meth2, donc peu importe que "nu" soit modifié par la suite.
vous pouvez essayer ceci:
il obtiendra la réponse que vous voulez
la source