Je suis tombé sur un comportement concernant l'opérateur ternaire C++ (?:
) qui (me) semble bizarre. Il est peut-être tout à fait normal, compte tenu que trois compilateurs différents ont le même comportement, mais je ne trouve pas d'explication satisfaisante.
Il porte sur le passage, en tant que paramètres d'un opérateur ternaire, d'un objet déjà instancié, et de l'instanciation à la volée d'un objet du même type. Le destructeur de l'objet déjà instancié est appelé à priori sans que cela ai lieu d'être.
Voici le source d'un programme mettant ce comportement en exergue :
#include <iostream>
class my_class {
protected:
char c_;
public:
my_class(char c) {
c_ = c;
std::cout << "Ctor: " << c_ << std::endl << std::flush;
}
~my_class() {
std::cout << "Dtor: " << c_ << std::endl << std::flush;
}
};
#define M(e)\
std::cout << std::endl << "true ? " << #e << std::endl << std::flush;\
(true ? e)
int main() {
my_class A('A'), B('B');
M(A : B);
M( A : my_class('C') );
M( my_class('D') : A );
M( my_class('E') : my_class('F') );
std::cout << std::endl << "End" << std::endl << std::flush;
}
Voici le résultat de ce programme. On y voit les paramètres de l'opérateur ternaire, et les éventuels appels aux constructeurs et/ou aux destructeurs.
Le texte en gras et italique est de mon fait, pour signaler là où je vois un problème.
Ctor: A
Ctor: Btrue ? A : B
true ? A : my_class('C')
Dtor: A --> Pourquoi le destructeur de l'objetA
est-il appelé ?true ? my_class('D') : A
Ctor: D
Dtor: Dtrue ? my_class('E') : my_class('F')
Ctor: E
Dtor: EEnd
Dtor: B
Dtor: A
Si vous voulez constater ce comportement de visu, il suffit de suivre ce lien : https://repl.it/@AtlasTK/C-bug
# Indice
Posté par Cyril Brulebois (site web personnel) . Évalué à 2.
Ajoute (un log dans) un constructeur de copie ?
Rien de spécifique à l'opérateur ternaire, je dirais.
;)
Debian Consultant @ DEBAMAX
[^] # Re: Indice
Posté par nazcafan . Évalué à 2. Dernière modification le 11 juin 2020 à 11:33.
L'indice de Cyril t'indique la bonne direction.
J'ai refait ton truc avec un constructeur de copie et en virant les
std::flush
et lesstd::endl
, parce que d'une part, dansendl
il y a déjà unflush
, et d'autre part, tu t'intéresses à la sortie standard du programme une fois terminé, alors tu n'as pas besoin de faire unflush
à chaque évènement quantique. Du coup c'est moins lourd à lire.Comme l'indiquait Cyril, une copie est effectuée quand tu invoques
true ? A : my_class('C')
et pas dans les autres cas. Cette copie est nécessaire pour que les deux arguments de l'opérateur ternaire soient de même type (my_class
).* Dans la ligne précédente, les deux arguments sont déjà du même type (
my_class &
) et aucune conversion n'est nécessaire ;* Dans la ligne suivante, une conversion serait nécessaire, mais elle n'a pas lieu parce qu'elle est codée dans la branche
false
de l'opérateur ternaire ;* Dans le dernier cas, les deux arguments sont de type (
my_class
) et aucune conversion n'est nécessaire.Un collègue sympa a réécrit le code avec des
template
pour mettre en évidence les types utilisés :Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.