36 votes

Mauvaise distribution - est-ce la distribution ou l'utilisation qui est un comportement indéfini ?

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

Vous avez du mal à comprendre ce que je demande ? Regardez 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 ou non être un Dog . Nous faisons toujours le static_cast de a à Dog * d mais nous n'utilisons jamais d à moins que nous soyons sûrs que c'est un Dog .

En supposant que a n'est pas un Dog Est-ce un comportement indéfini au moment de la distribution ? Ou est-il défini car 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 que ce n'est probablement pas un code génial, mais je suis plus intéressé de savoir si c'est valide).

3 votes

Réponse courte : elle n'est pas valable. Réponse plus longue : cela dépend de la manière dont toutes les parties manquantes sont effectivement réalisées. Veuillez poster un exemple minimal reproductible qui compile.

9 votes

Normalement, je suis tout à fait favorable à MVCE, mais je ne suis pas convaincu que cela soit vraiment utile. Je ne donne vraiment le code d'exemple que pour aider à expliquer la question - je ne me soucie pas vraiment du code inclus. En fait, c'est le contraire - je serais extrêmement intéressé par le contraire - en supposant des implémentations raisonnables de Animal, Dog, Cat, j'aimerais bien savoir ce qui ferait une différence avec la réponse comme vous le dites.

0 votes

@wally J'ai modifié ma question pour la rendre plus claire.

36voto

Angew Points 53063

La distribution elle-même a un comportement indéfini. Citation de C++17 (n4659) [expr.static.cast] 8.2.10/11 :

Une valeur prvalue de type "pointeur vers cv1 B ", où B est un type de classe, peut être converti en une valeur de type "pointeur". vers cv2 D ", où D est une classe dérivée (clause 13) de B si cv2 est identique ou supérieur à la qualification cv cv-qualification que, cv1 . ... Si la prvalue de de type "pointeur vers cv1 B "Les points de repère sont les suivants 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 est indéfini.

0 votes

Merci. Avant de résoudre, il y a un commentaire de Wally disant que cela dépend. Pouvez-vous voir quelque chose qui rendrait cela défini puisque votre réponse est plutôt explicite ? De même, "le comportement est indéfini" - cela signifie-t-il que le comportement du cast est indéfini (dans ce cas, je suis d'accord car je n'utilise pas le résultat de celui-ci) ou le programme entier ?

3 votes

Si je ne me trompe pas, OP serait en sécurité avec une reinterpret_cast n'est-ce pas ?

1 votes

@MikeVine Je pense qu'un comportement indéfini signifie que votre contrat avec la norme ne doit pas être honoré pour l'ensemble du programme. En pratique, je ne vois pas cela formater votre chat.

12voto

YSC Points 3386

Il s'agit d'un comportement non défini mais (assez drôle) si vous aviez utilisé reinterpret_cast au lieu de static_cast tu rejetterais 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 prvalue v de type pointeur d'objet est convertie en un pointeur d'objet de type "pointeur vers cv T ", le résultat est static_­cast<cv T*>(static_­cast<cv void*>(v)) .

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

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

[class.mem]/26

Si un objet de classe 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 l'objet séquence initiale commune de Animal et Dog le comportement est défini.

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

0 votes

Les commentaires ne sont pas destinés à une discussion approfondie ; cette conversation a été déplacé vers le chat .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X