web-dev-qa-db-fra.com

Cast incorrect - est-ce le cast ou l'utilisation qui est un comportement non défini

Si j'effectue un transtypage d'une base vers un type dérivé, mais que le type de base n'est pas une instance de type dérivé, mais n'utilise le résultat que s'il l'est, est-ce que j'obtiens un comportement non défini?

Difficile de comprendre ce que je demande? jetez un oeil à cet exemple:

struct Animal { int GetType(){...} };
struct Dog : Animal { bool HasLoudBark(){...}};
struct Cat : Animal { bool HasEvilStare(){...} };

Animal * a = ...;
Dog* d = static_cast<Dog*>(a);

if(a->GetType() == DogType && d->HasLoudBark())
    ....

Dans ce cas, a peut être ou non Dog. Nous faisons toujours le static_cast de a à Dog * d mais nous n'utilisons jamais d sauf si nous sommes sûrs que c'est un Dog.

En supposant que a n'est pas un Dog, ce comportement est-il indéfini au moment de la distribution? Ou est-il défini comme nous n'utilisons pas réellement d à moins qu'il ne s'agisse vraiment d'un Dog?

Les références aux parties pertinentes de la norme sont appréciées.

(Oui, je sais que je peux utiliser dynamic_cast et RTTI, et ce n'est probablement pas du bon code, mais je suis plus intéressé à savoir si cela est valide)

36
Mike Vine

Le casting lui-même a un comportement indéfini. Citant C++ 17 (n4659) [expr.static.cast] 8.2.10/11:

Une valeur de type "pointeur vers cv1B", où B est un type de classe, peut être convertie en une valeur de type "pointeur vers cv2D ”, où D est une classe dérivée (article 13) de B, si cv2 est la même qualification cv as, ou supérieur à la qualification cv, cv1. ... Si la valeur de type "pointeur vers cv1B" pointe vers un B qui est en fait un sous-objet d'un objet de type D, le pointeur résultant pointe vers l'objet englobant de type D. Sinon, le comportement n'est pas défini.

36
Angew

C'est un comportement indéfini , mais (assez drôle) si vous aviez utilisé reinterpret_cast Au lieu de static_cast, Vous rejetteriez ce démon.

[expr.reinterpret.cast]/7

Un pointeur d'objet peut être explicitement converti en un pointeur d'objet d'un type différent. Lorsqu'une valeur v du type de pointeur d'objet est convertie en type de pointeur d'objet "pointeur vers cv T", Le résultat est static_­cast<cv T*>(static_­cast<cv void*>(v)).

Comme l'a noté l'utilisateur Angew, cela "nécessite une représentation interne particulière qui garantit que static_cast<void*>(d) == static_cast<void*>(a) lorsque a == d".

Ceci est exprimé par [class.mem]/22 En 26:

[class.mem]/26

Si un objet de classe à présentation standard possède des membres de données non statiques, son adresse est la même que l'adresse de son premier membre de données non statiques si ce membre n'est pas un champ de bits. Son adresse est également la même que l'adresse de chacun de ses sous-objets de classe de base.

Donc, si GetType() de Animal renvoie la valeur d'un membre de données non statique de la séquence initiale commune de Animal et Dog, le comportement est défini.

Ces exigences sont remplies lorsqu'il s'agit d'héritage simple et d'objets alignés par défaut.

12
YSC