C ++ 0x spowoduje, że następujący i podobny kod będzie źle sformułowany, ponieważ wymaga tak zwanej konwersji zawężającej a double
do a int
.
int a[] = { 1.0 };
Zastanawiam się, czy ten rodzaj inicjalizacji jest często używany w kodzie świata rzeczywistego. Ile kodów zostanie uszkodzonych przez tę zmianę? Czy naprawienie tego w kodzie wymaga wiele wysiłku, jeśli w ogóle ma to wpływ na kod?
Dla odniesienia, patrz 8.5.4 / 6 n3225
Konwersja zawężająca jest konwersją niejawną
- z typu zmiennoprzecinkowego na typ całkowity lub
- od long double do double lub float, lub od double do float, z wyjątkiem sytuacji, gdy źródłem jest wyrażenie stałe, a rzeczywista wartość po konwersji mieści się w zakresie wartości, które można przedstawić (nawet jeśli nie można jej dokładnie przedstawić) lub
- z typu całkowitego lub typu wyliczenia bez zakresu do typu zmiennoprzecinkowego, z wyjątkiem sytuacji, gdy źródło jest wyrażeniem stałym, a rzeczywista wartość po konwersji będzie pasować do typu docelowego i utworzy oryginalną wartość po przekonwertowaniu z powrotem na typ oryginalny lub
- z typu całkowitego lub typu wyliczenia bez zakresu do typu całkowitego, który nie może reprezentować wszystkich wartości typu oryginalnego, z wyjątkiem sytuacji, gdy źródło jest wyrażeniem stałym, a rzeczywista wartość po konwersji będzie pasować do typu docelowego i wytworzy oryginalną wartość, gdy przekonwertowany z powrotem na oryginalny typ.
c++
c++11
survey
aggregate-initialization
Johannes Schaub - litb
źródło
źródło
0
tak już jestint
.){
inicjatorach nawiasów klamrowych}
, a jedyne starsze użycie ich dotyczy tablic i struktur POD. Ponadto, jeśli istniejący kod ma wyraźne rzutowania tam, gdzie należą, nie ulegnie awarii.int a = 1.0;
jest nadal aktualny.Odpowiedzi:
Wpadłem na tę przełomową zmianę, kiedy użyłem GCC. Kompilator wypisał błąd dla takiego kodu:
void foo(const unsigned long long &i) { unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32}; }
Na szczęście komunikaty o błędach były proste, a poprawka była prosta:
void foo(const unsigned long long &i) { unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF), static_cast<unsigned int>(i >> 32)}; }
Kod znajdował się w zewnętrznej bibliotece, a tylko dwa wystąpienia w jednym pliku. Nie sądzę, aby ta znacząca zmiana wpłynęła na znaczną część kodu. Nowicjusze mogą się mylić, choć.
źródło
Byłbym zaskoczony i rozczarowany, gdybym się dowiedział, że którykolwiek z kodów C ++, które napisałem w ciągu ostatnich 12 lat, miał tego rodzaju problem. Jednak większość kompilatorów przez cały czas wyrzucałaby ostrzeżenia o wszelkich „zawężeniach” w czasie kompilacji, chyba że czegoś mi brakuje.
Czy to również zawężające konwersje?
unsigned short b[] = { -1, INT_MAX };
Jeśli tak, myślę, że mogą pojawiać się nieco częściej niż przykład typu zmiennoprzecinkowego na typ całkowy.
źródło
Nie zdziwiłbym się, gdyby kogoś złapało coś takiego:
float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};
(w mojej implementacji ostatnie dwa nie dają tego samego wyniku po przekonwertowaniu z powrotem na int / long, dlatego są zawężane)
Jednak nie pamiętam, bym kiedykolwiek to pisał. Jest to przydatne tylko wtedy, gdy przybliżenie granic jest do czegoś przydatne.
Wydaje się to również co najmniej niejasno prawdopodobne:
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 }
ale nie jest to całkowicie przekonujące, ponieważ jeśli wiem, że mam dokładnie dwie wartości, po co umieszczać je w tablicach, a nie tylko
float floatval1 = val1, floatval1 = val2;
? Jaka jest jednak motywacja, dlaczego to powinno się kompilować (i działać, pod warunkiem, że utrata precyzji mieści się w dopuszczalnej dokładności dla programu), afloat asfloat[] = {val1, val2};
nie powinno? Tak czy inaczej, inicjalizuję dwa elementy zmiennoprzecinkowe z dwóch liczb całkowitych, po prostu w jednym przypadku te dwa elementy zmiennoprzecinkowe są członkami agregatu.Wydaje się to szczególnie trudne w przypadkach, gdy wyrażenie niestałe powoduje zawężenie konwersji, mimo że (w określonej implementacji) wszystkie wartości typu źródłowego są reprezentowane w typie docelowym i konwertowane z powrotem do ich oryginalnych wartości:
char i = something(); static_assert(CHAR_BIT == 8); double ra[] = {i}; // how is this worse than using a constant value?
Zakładając, że nie ma błędu, prawdopodobnie poprawka polega na tym, aby konwersja była jawna. Jeśli nie robisz czegoś dziwnego z makrami, myślę, że inicjator tablicy pojawia się tylko blisko typu tablicy lub przynajmniej do czegoś, co reprezentuje typ, co może zależeć od parametru szablonu. Więc rzut powinien być łatwy, jeśli jest szczegółowy.
źródło
Praktyczny przypadek, z którym się spotkałem:
float x = 4.2; // an input argument float a[2] = {x-0.5, x+0.5};
Literał numeryczny jest niejawnie,
double
który powoduje awans.źródło
float
pisząc0.5f
. ;)float
był to typedef lub parametr szablonu (przynajmniej bez utraty precyzji), ale chodzi o to, że napisany kod działał z poprawną semantyką i stał się błędem w C ++ 11. To znaczy definicja „przełomowej zmiany”.Spróbuj dodać -Wno-zawężanie do swoich CFLAGS, na przykład:
CFLAGS += -std=c++0x -Wno-narrowing
źródło
Zawężające błędy konwersji źle współdziałają z niejawnymi regułami promocji liczb całkowitych.
Wystąpił błąd z kodem, który wyglądał jak
struct char_t { char a; } void function(char c, char d) { char_t a = { c+d }; }
Co powoduje zawężający się błąd konwersji (który jest poprawny zgodnie ze standardem). Powodem jest to, że
c
id
niejawnie awansować doint
i uzyskanyint
nie może być zawężony plecami do char w liście inicjatora.OTOH
void function(char c, char d) { char a = c+d; }
jest oczywiście nadal w porządku (w przeciwnym razie rozpętałoby się piekło). Ale, co zaskakujące, nawet
template<char c, char d> void function() { char_t a = { c+d }; }
jest w porządku i kompiluje się bez ostrzeżenia, jeśli suma cid jest mniejsza niż CHAR_MAX. Nadal uważam, że jest to wada w C ++ 11, ale ludzie myślą inaczej - prawdopodobnie dlatego, że nie jest to łatwe do naprawienia bez pozbycia się niejawnej konwersji liczb całkowitych (która jest reliktem z przeszłości, kiedy ludzie pisali kod jak
char a=b*c/d
i spodziewałem się, że zadziała, nawet jeśli (b * c)> CHAR_MAX) lub zawężenie błędów konwersji (które są prawdopodobnie dobrą rzeczą).źródło
unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };
<- zawężająca konwersja wewnątrz {}. Naprawdę? Więc operator & również niejawnie konwertuje znaki bez znaku na int? Cóż, nie obchodzi mnie to, wynik jest nadal gwarantowany jako niepodpisany char, argh.To była rzeczywiście przełomowa zmiana, ponieważ doświadczenie z prawdziwego życia z tą funkcją pokazało, że gcc zmieniło zawężenie w ostrzeżenie o błędzie w wielu przypadkach z powodu prawdziwych problemów związanych z przenoszeniem baz kodu z C ++ 03 do C ++ 11. Zobacz ten komentarz w raporcie o błędzie gcc :
źródło
Wygląda na to, że GCC-4.7 nie wyświetla już błędów przy zawężaniu konwersji, ale zamiast tego wyświetla ostrzeżenia.
źródło