J'ai fait un petit test récemment, et je voudrais discuter du résultat.
voici mon petit code :
#include <vector>
std::vector<int> fonc()
{
std::vector<int> res;
res.push_back(53);
res.push_back(37);
return res;
}
int main()
{
const std::vector<int> V = fonc();
//V.clear();
((std::vector<int>)V).clear();
size_t a = V.size();
return 0;
}
ma fonction fonc construit un vector de 2 elements et le retourne.
Je recupere ça dans le main, dans un const vector (je le verouille en écriture).
2e ligne (commentée) je fais V.clear() -> ça ne compile pas. Normal, clear n'est pas une méthode const (normal), donc ça ne compile pas. Jusque la je suis d'accord. Je commente cette ligne
3e ligne, je cast sauvagement le const vector en vector -> déprotection par le cast. Avant de compiler, je m'étais parié que ça ne compilerais pas !! Qu'il me dirait "cannot convert const to non-const"
et ben non !!!! Il compile !!
La je hurle, je me dis "on peut facilement faire péter les mécanismes de protection".
Je lance le clear, ça compile.
Heureusement, 4e ligne, je teste la size : V n'a pas été touché. clear n'a "pas marché".
Je pousse un soupir de soulagement : "non, on ne peut pas faire péter les mécanismes de protection si facilement"
Et la, je trace en debug. Qu'a fait clear ?
Je constate que le cast de V en vector non const a appelé le constructeur de recopie. Il a donc été créée une variable temporaire, copie de V, et cette variable a subie le clear...
Maintenant, ma question : pourquoi ce cast C-style a t il appelé le constructeur de recopie ?
Est ce que tout cast C-style appelle des constructeurs par recopie ??
Qu'en est il de static_cast ?
Quels sont les mécanismes sousjascents mis en jeu ?
Lors d'un cast, tu crèes de toute façon TOUJOURS une nouvelle variable, le plus souvent temporaire comme ici.
Tu ne peux pas modifier la nature intrinsèque d'un objet, mais tu peux créer une nouvelle variable (objet) qui reprend tout ou partie du contenu de la 1ère en faisant un cast.
const_cast crèe une nouvelle variable non constante.
static_cast fait un cast "à la C".
dynamic_cast transforme un pointeur sur mère en fille (entre autre).
reinterpret_cast transforme les données brutes en autre chose.
Mais tu créeras dans tous les cas une nouvelle variable.
const_cast ne crée pas de nouvelle variable, il essaie (!) de donner une vue modifiable sur une donnée qui officiellement ne l'est pas.
Suivant le contexte, et des subtilités vicieuses [*], un cast à la C peut résulter en un des 3 casts qui n'est pas un reinterpret_cast.
Maintenant, est-ce qu'il est normal que le (T)kv (avec kv de type "T const") se comporte comme un T(kv), j'avoue ne mettre jamais posé la question. [Je dirais que c'est le genre de trucs à vérifier dans la norme ou sur fclc++]
[*] Un cast C sur un pointeur entre types dont on n'a qu'une déclaration (anticipée) va provoquer un reinterpret_cast au lieu d'un static_cast.
EDIT: plein de choses dans la FAQ comeau que j'ai la flemme de relire.
Leur valeur est la même. Et je ne vois pas (enfin, je ne suis pas allé fouiller dans la norme pour ça) ce qui empêcherait le compilo de leur donner la même adresse tant que dans la portée concernée ils continuent tous les deux de pointer sur la même donnée.
Maintenant, c'est vrai que l'expression de l'OP est (T)kv et non (T&)kv ... hum ... elle viendrait de là la copie ?
Si le compilo peut voir que sur toute leur portée les deux pointeurs sont sensés pointer sur la même adresse. AMA, il n'est pas obliger de définir une vraie nouvelle variable (pointeur) sur la pile. Pourquoi ne pourrait-il pas considérer l'autre pointeur comme un simple alias (à l'image des références).
La notion de const n'étant de toutes façon qu'un garde barrière vérifié au moment de la compilation. Après, le code machine final n'en sait plus rien.
Un constructeur n'ayant qu'un seul paramètre peut agir comme un operateur de conversion, implicite par défaut, explicite si spécifié. Comme il n'existe pas d'opérateur de conv de const en non-const pour vector, le compilo se sert du constructeur qui accepte un const vector en param, donc le constructeur par copie.
class T1 {
public:
T1() {}
T1(const T1 &) {}
};
class T2 {
public:
T2() {};
T2(const T1 &) {}
T2(const T2 &) {}
};
class T3 : T2 {
public:
T3() {}
explicit T3(const T2 &) {}
operator T1 & () {return *((T1*)(this));}
};
void main() {
T1 t1;
T2 t2;
T3 t3;
t2 = t1; // Appel du CTor de T2
//t3 = t2; // ne compile pas car CTor de T3 explicit
t3 = T3(t1); // conversion implicite de t1 en T2
t1 = (T1)t3; // Appel du CTor par copie de T1 avec conv de t3 en T1
}
Avant tout merci pour vos réponses.
Si Spaz a raison, ceci expliquerait cela. Bien entendu, j'aimerais en etre sur, mais je ne vois pas d'autres explications.
Avant de marquer le sujet comme résolu, si quelqu'un a un lien vers une regle officielle la dessus, je suis preneur.
J'ai quand meme essayé la chose suivante par la suite (me disant que ça venait du cast C-style :
((static_cast<std::vector<int> >(V)).clear();
Et il se trouve que je tombe sur le meme résultat : le compilo passe, la fonction clear n'affecte pas V, mais visiblement une copie faite a la volée, dont l'existance n'est que temporaire dans cette ligne.
Avec ce "piege" comme quoi un bete cast pourrait entrainer une recopie, j'ai soudain comme un doute sur par mal de projets que j'ai fait (perso ET professionnels) en me disant que peut etre que je fais parfois des recopies non voulues et inutiles sans le savoir...
Bah... si c'est le cas, et que j'en trouve des occurrence, les clients seront content des prochaines versions qui iront plus vite... (si la différence se perçoit...)
Merci a vous tous !
Si vous avez la preuve quelque part sur un lien officiel que le cast peut recopier, je suis preneur, et ce sera la résolution du topic !
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html