web-dev-qa-db-fra.com

Pourquoi ne puis-je pas static_cast entre char * et char non signé *?

Apparemment, le compilateur les considère comme des types non liés et donc reinterpret_cast est requis. Pourquoi est-ce la règle?

40
Nick

Ce sont des types complètement différents voir standard:

3.9.1 Types fondamentaux [basic.fundamental]

1 Les objets déclarés comme caractères char) doivent être suffisamment grands pour stocker tout membre du jeu de caractères de base de l'implémentation. Si un caractère de cet ensemble est stocké dans un objet caractère, la valeur intégrale de cet objet caractère est égale à la valeur de la forme littérale à caractère unique de ce caractère. Il est défini par l'implémentation si un objet char peut contenir des valeurs négatives. Les caractères peuvent être explicitement déclarés non signés ou
signé. Le caractère ordinaire, le caractère signé et le caractère non signé sont trois types distincts. Un caractère, un caractère signé et un caractère les caractères non signés occupent la même quantité de stockage et ont les mêmes exigences d'alignement ( basic.types); c'est-à-dire qu'ils ont la même représentation d'objet. Pour les types de caractères, tous les bits de l'objet
la représentation participe à la représentation de la valeur. Pour les types de caractères non signés, tous les modèles de bits possibles de la représentation de valeur représentent des nombres. Ces exigences ne s'appliquent pas aux autres types. Dans toute implémentation particulière, un objet char simple peut prendre les mêmes valeurs qu'un char signé ou un char non signé; lequel est défini par l'implémentation.

Si analogue à cela est également la raison pour laquelle ce qui suit échoue:

unsigned int* a = new unsigned int(10);
int* b = static_cast<int*>(a); // error different types

a et b sont des types complètement différents, vraiment ce que vous vous demandez, c'est pourquoi static_cast est si restrictif quand il peut effectuer les opérations suivantes sans problème

unsigned int a = new unsigned int(10);
int b = static_cast<int>(a); // OK but may result in loss of precision

et pourquoi ne peut-il pas en déduire que les types cibles ont la même largeur de champ binaire et peuvent être représentés? Il peut le faire pour les types scalaires mais pour les pointeurs, à moins que la cible ne soit dérivée de la source et que vous souhaitiez effectuer un abaissement, puis la conversion entre les pointeurs ne fonctionnera pas.

Bjarne Stroustrop explique pourquoi static_cast sont utiles dans ce lien: http://www.stroustrup.com/bs_faq2.html#static-cast mais sous une forme abrégée, il appartient à l'utilisateur d'indiquer clairement ses intentions et de donnez au compilateur la possibilité de vérifier que ce que vous envisagez peut être réalisé, puisque static_cast ne prend pas en charge la conversion entre différents types de pointeurs, le compilateur peut détecter cette erreur pour alerter l'utilisateur et s'il souhaite vraiment effectuer cette conversion, il doit alors utiliser reinterpret_cast.

30
EdChum

vous essayez de convertir des pointeurs indépendants avec un static_cast. Ce n'est pas le but de static_cast. Ici vous pouvez voir: Type Casting .

Avec static_cast, vous pouvez convertir des données numériques (par exemple, un caractère en caractère non signé devrait fonctionner) ou un pointeur vers des classes liées (liées par un héritage). Ce n'est pas le cas. Vous souhaitez convertir un pointeur non lié en un autre, vous devez donc utiliser reinterpret_cast.

Fondamentalement, ce que vous essayez de faire est pour le compilateur la même chose que d'essayer de convertir un char * en void *.


Ok, voici quelques réflexions supplémentaires pourquoi autoriser cela est fondamentalement mauvais. static_cast peut être utilisé pour convertir des types numériques entre eux. Il est donc parfaitement légal d'écrire ce qui suit:

char x = 5;
unsigned char y = static_cast<unsigned char>(x);

ce qui est également possible:

double d = 1.2;
int i = static_cast<int>(d);

Si vous regardez ce code dans l'assembleur, vous verrez que la deuxième distribution n'est pas une simple réinterprétation du modèle binaire de d mais à la place quelques instructions d'assembleur pour les conversions sont insérées ici.

Maintenant, si nous étendons ce comportement aux tableaux, le cas où une simple façon différente d'interpréter le modèle de bits est suffisant, cela pourrait fonctionner. Mais qu'en est-il de la conversion de tableaux de doubles en tableaux d'ints? C'est là que vous devez déclarer que vous voulez simplement une réinterprétation des modèles de bits - il existe un mécanisme pour cela appelé reinterpret_cast, ou vous devez faire un travail supplémentaire. Comme vous pouvez le voir, une simple extension de static_cast pour le pointeur/les tableaux n'est pas suffisante car elle doit se comporter de la même manière que les valeurs individuelles de static_casting des types. Cela nécessite parfois du code supplémentaire et il n'est pas clairement défini comment cela doit être fait pour les tableaux. Dans votre cas - arrêt à\0 - parce que c'est la convention? Ce n'est pas suffisant pour les cas non-string (nombre). Que se passera-t-il si la taille du type de données change (par exemple, int vs double sur x86-32bit)?

Le comportement que vous souhaitez ne peut pas être correctement défini pour tous les cas d'utilisation, c'est pourquoi il n'est pas dans la norme C++. Sinon, vous devez vous rappeler des choses comme: "je peux convertir ce type en l'autre tant qu'ils sont de type entier, ont la même largeur et ...". De cette façon, c'est tout à fait clair - soit il s'agit de CLASSES liées - alors vous pouvez lancer les pointeurs, ou ce sont des types numériques - alors vous pouvez lancer les valeurs.

8
Tobias Langner

En plus d'être des pointeurs, unsigned char * et char * n'ont rien en commun (EdChum a déjà mentionné le fait que char, signed char et unsigned char sont de trois types différents). On pourrait dire la même chose pour Foo * et Bar * types de pointeurs vers des structures différentes.

static_cast signifie qu'un pointeur du type source peut être utilisé comme pointeur du type de destination, ce qui nécessite une relation de sous-type. Par conséquent, il ne peut pas être utilisé dans le contexte de votre question; ce dont vous avez besoin est soit reinterpret_cast qui fait exactement ce que vous voulez ou une distribution de style C.

4
Michael Foukarakis