C ++ 0x va rendre le code suivant et le code similaire mal formé, car il nécessite une conversion dite restrictive de a double
en a int
.
int a[] = { 1.0 };
Je me demande si ce type d'initialisation est beaucoup utilisé dans le code du monde réel. Combien de code sera brisé par ce changement? Y a-t-il beaucoup d'efforts pour résoudre ce problème dans votre code, si votre code est affecté du tout?
Pour référence, voir 8.5.4 / 6 de n3225
Une conversion restrictive est une conversion implicite
- d'un type à virgule flottante à un type entier, ou
- de long double à double ou float, ou de double à float, sauf lorsque la source est une expression constante et que la valeur réelle après conversion se situe dans la plage de valeurs pouvant être représentée (même si elle ne peut pas être représentée exactement), ou
- d'un type entier ou d'un type d'énumération non scopé à un type à virgule flottante, sauf lorsque la source est une expression constante et que la valeur réelle après conversion rentrera dans le type cible et produira la valeur d'origine lorsqu'elle sera reconvertie au type d'origine, ou
- d'un type entier ou d'un type d'énumération non scopé à un type entier qui ne peut pas représenter toutes les valeurs du type d'origine, sauf lorsque la source est une expression constante et la valeur réelle après la conversion rentrera dans le type cible et produira la valeur d'origine lorsque reconverti au type d'origine.
c++
c++11
survey
aggregate-initialization
Johannes Schaub - litb
la source
la source
0
c'est déjà un deint
toute façon.){
les initialiseurs d'accolades}
, et que la seule utilisation héritée de ceux-ci est pour les tableaux et les structures POD. De plus, si le code existant a des casts explicites à leur place, il ne se cassera pas.int a = 1.0;
est toujours valide.Réponses:
J'ai rencontré ce changement radical lorsque j'ai utilisé GCC. Le compilateur a imprimé une erreur pour le code comme ceci:
void foo(const unsigned long long &i) { unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32}; }
Heureusement, les messages d'erreur étaient simples et le correctif était simple:
void foo(const unsigned long long &i) { unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF), static_cast<unsigned int>(i >> 32)}; }
Le code était dans une bibliothèque externe, avec seulement deux occurrences dans un fichier. Je ne pense pas que le changement de rupture affectera beaucoup le code. Les novices pourraient cependant être confus .
la source
Je serais surpris et déçu de moi-même d'apprendre que l'un des codes C ++ que j'ai écrits au cours des 12 dernières années avait ce genre de problème. Mais la plupart des compilateurs auraient émis des avertissements sur tout "rétrécissement" au moment de la compilation, à moins que je ne manque quelque chose.
S'agit-il également de conversions réductrices?
unsigned short b[] = { -1, INT_MAX };
Si tel est le cas, je pense qu'ils pourraient apparaître un peu plus souvent que votre exemple de type flottant à type intégral.
la source
Je ne serais pas surpris si quelqu'un se faisait prendre par quelque chose comme:
float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};
(sur mon implémentation, les deux derniers ne produisent pas le même résultat lorsqu'ils sont reconvertis en int / long, donc se rétrécissent)
Cependant, je ne me souviens pas avoir écrit cela. Ce n'est utile que si une approximation des limites est utile pour quelque chose.
Cela semble au moins vaguement plausible aussi:
void some_function(int val1, int val2) { float asfloat[] = {val1, val2}; // not in C++0x double asdouble[] = {val1, val2}; // not in C++0x int asint[] = {val1, val2}; // OK // now do something with the arrays }
mais ce n'est pas tout à fait convaincant, car si je sais que j'ai exactement deux valeurs, pourquoi les mettre dans des tableaux plutôt que simplement
float floatval1 = val1, floatval1 = val2;
? Quelle est la motivation, cependant, pourquoi cela devrait compiler (et fonctionner, à condition que la perte de précision soit dans une précision acceptable pour le programme), alors quefloat asfloat[] = {val1, val2};
non? Quoi qu'il en soit, j'initialise deux flottants à partir de deux entiers, c'est juste que dans un cas, les deux flottants sont membres d'un agrégat.Cela semble particulièrement sévère dans les cas où une expression non constante entraîne une conversion restrictive même si (sur une implémentation particulière), toutes les valeurs du type source sont représentables dans le type de destination et convertibles à leurs valeurs d'origine:
char i = something(); static_assert(CHAR_BIT == 8); double ra[] = {i}; // how is this worse than using a constant value?
En supposant qu'il n'y ait pas de bogue, le correctif est probablement toujours de rendre la conversion explicite. À moins que vous ne fassiez quelque chose d'étrange avec les macros, je pense qu'un initialiseur de tableau n'apparaît qu'à proximité du type du tableau, ou du moins de quelque chose représentant le type, qui pourrait dépendre d'un paramètre de modèle. Donc, un casting devrait être facile, même si détaillé.
la source
Un exemple pratique que j'ai rencontré:
float x = 4.2; // an input argument float a[2] = {x-0.5, x+0.5};
Le littéral numérique est implicitement
double
ce qui provoque la promotion.la source
float
en écrivant0.5f
. ;)float
s'agissait d'un paramètre typedef ou template (au moins sans perte de précision), mais le fait est que le code tel qu'il est écrit fonctionnait avec la sémantique correcte et est devenu une erreur avec C ++ 11. C'est-à-dire la définition d'un «changement de rupture».Essayez d'ajouter -Wno-narrowing à votre CFLAGS, par exemple:
CFLAGS += -std=c++0x -Wno-narrowing
la source
Les erreurs de conversion restrictives interagissent mal avec les règles de promotion d'entiers implicites.
J'ai eu une erreur avec le code qui ressemblait à
struct char_t { char a; } void function(char c, char d) { char_t a = { c+d }; }
Ce qui produit une erreur de conversion de rétrécissement (qui est correcte selon la norme). La raison en est que
c
etd
implicitement être promu versint
et que le résultatint
n'est pas autorisé à être réduit à char dans une liste d'initialiseurs.OTOH
void function(char c, char d) { char a = c+d; }
est bien sûr toujours bien (sinon tout l'enfer se déchaînerait). Mais étonnamment, même
template<char c, char d> void function() { char_t a = { c+d }; }
est ok et compile sans avertissement si la somme de c et d est inférieure à CHAR_MAX. Je pense toujours que c'est un défaut de C ++ 11, mais les gens pensent le contraire - peut-être parce qu'il n'est pas facile à réparer sans se débarrasser de l'une ou l'autre des conversions entières implicites (qui est une relique du passé, lorsque les gens ont écrit du code comme
char a=b*c/d
et je m'attendais à ce que cela fonctionne même si (b * c)> CHAR_MAX) ou en réduisant les erreurs de conversion (ce qui est peut-être une bonne chose).la source
unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };
<- rétrécissement de la conversion à l'intérieur {}. Vraiment? Ainsi, l'opérateur & convertit également implicitement les caractères non signés en int? Eh bien, je m'en fiche, le résultat est toujours garanti comme étant un caractère non signé, argh.C'était en effet un changement radical car l'expérience réelle de cette fonctionnalité a montré que gcc avait transformé le rétrécissement en un avertissement d'une erreur dans de nombreux cas en raison de problèmes réels liés au portage des bases de code C ++ 03 vers C ++ 11. Voir ce commentaire dans un rapport de bogue gcc :
la source
Il semble que GCC-4.7 ne donne plus d'erreurs pour réduire les conversions, mais des avertissements à la place.
la source