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
n'est-il pas 1 lorsque meth2()
démarre?
Parce que l'ordre d'évaluation n'est pas spécifié.
Vous voyez nu
dans main
en cours d'évaluation en 0
avant même meth1
est 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:
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu);
c.meth2(nu);
}
Je pense que cette partie du projet de norme concernant l'ordre d'évaluation est pertinente:
1.9 Exécution du programme
...
- Sauf indication contraire, les évaluations d'opérandes d'opérateurs individuels et de sous-expressions d'expressions individuelles ne sont pas séquencées. Les calculs de valeur des opérandes d'un opérateur sont séquencés avant le calcul de valeur du résultat de l'opérateur. Si un effet secondaire sur un objet scalaire n'est pas séquencé par rapport à un autre effet secondaire sur le même objet scalaire ou à un calcul de valeur utilisant la valeur du même objet scalaire, et qu'ils ne sont pas potentiellement concurrents, le comportement n'est pas défini
et aussi:
5.2.2 Appel de fonction
...
- [Remarque: Les évaluations de l'expression postfixe et des arguments ne sont pas séquencées les unes par rapport aux autres. Tous les effets secondaires des évaluations d'arguments sont séquencés avant la fonction est entré - note de fin]
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
, Donc nous voyons clairement la répartition dans l'expression et l'argument postfix nu
:
operator()(c.meth1(&nu).meth2, nu);
Les évaluations de l'expression et de l'argument postfixe pour l'appel de fonction final (c'est-à-dire l'expression postfixe c.meth1(&nu).meth2
et nu
) sont non séquencées par rapport à les uns les autres selon la règle d'appel de fonction ci-dessus. Par conséquent, le effet secondaire du calcul de l'expression postfixe sur l'objet scalaire ar
n'est pas séquencé par rapport à l'évaluation de l'argument de nu
avant le 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'argument nu
à l'appel meth2
Après l'appel meth1
- il est libre de ne supposer aucun effet secondaire de meth1
Affecte l'évaluation de nu
.
Le code d'assemblage produit par ce qui précède contient la séquence suivante dans la fonction main
:
nu
est allouée sur la pile et initialisée avec 0.ebx
dans mon cas) reçoit une copie de la valeur de nu
nu
et c
sont chargées dans les registres de paramètresmeth1
Est appelénu
dans le registre ebx
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 en cache de 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é que nu
ait pu être changé 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 d'opérande n'étant pas séquencé avant l'appel de fonction final était incorrecte, car elles le sont. Le problème est le fait que le calcul des opérandes eux-mêmes est séquencé de façon indéterminée.
Dans la norme C++ de 1998, section 5, paragraphe 4
Sauf indication contraire, l'ordre d'évaluation des opérandes des opérateurs individuels et des sous-expressions des expressions individuelles, ainsi que l'ordre dans lequel les effets secondaires se produisent, n'est pas spécifié. Entre le point de séquence précédent et suivant, un objet scalaire doit voir sa valeur stockée modifiée au plus une fois par l'évaluation d'une expression. En outre, la valeur antérieure ne doit être consultée que pour déterminer la valeur à stocker. Les exigences du présent paragraphe doivent être satisfaites pour chaque classement autorisé des sous-expressions d'une expression complète; sinon, le comportement n'est pas défini.
(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 d'appeler c1::meth1()
et nu
doit être évalué avant d'appeler c1::meth2()
. Il n'est cependant pas nécessaire que nu
soit évalué avant &nu
(Par exemple, il est permis que nu
soit évalué en premier, puis &nu
, Et ensuite c1::meth1()
est appelée - ce qui pourrait être ce que fait votre compilateur). L'expression *ar = 1
Dans c1::meth1()
n'est donc pas garantie d'être évaluée avant que nu
dans main()
soit évaluée, afin d'être passée à c1::meth2()
.
Les normes C++ ultérieures (que je n'ai pas actuellement sur le PC que j'utilise ce soir) ont essentiellement la même clause.
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é transmis. Je veux dire quand vous utilisez "c.meth1 (& nu) .meth2 (nu);" la valeur nu = 0 a été transmise à meth2, donc peu importe que "nu" soit modifié en dernier.
vous pouvez essayer ceci:
#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);
getchar();
}
il obtiendra la réponse que vous souhaitez