Perché la limitazione della conversione utilizzata con l’inizializzatore delimitato da parentesi graffa non causa un errore?

Ho saputo dell’inizializzatore delimitato da parentesi graffa in The C ++ Programming Language, 4th ed. > Capitolo 2: A Tour of C ++: The Basics.

Sto citando dal libro qui sotto.

La forma = è tradizionale e risale a C, ma in caso di dubbio, utilizzare il modulo generale {} -list (§6.3.5.2). Se non altro, ti salva dalle conversioni che perdono informazioni (restringendo le conversioni; §10.5):

int i1 = 7.2; // i1 becomes 7 int i2 {7.2}; // error : floating-point to integer conversion int i3 = {7.2}; // error : floating-point to integer conversion (the = is redundant) 

Tuttavia, non sono in grado di riprodurre questi risultati.

Ho il codice seguente.

 #include  int main() { int i1 = 7.2; int i2 {7.2}; int i3 = {7.2}; std::cout << i1 << "\n"; std::cout << i2 << "\n"; std::cout << i3 << "\n"; } 

Quando lo compilo ed eseguo, non ho alcun errore. Ricevo un avvertimento su std=c++11 ma nessun errore.

 $ g++ init.cpp init.cpp: In function 'int main()': init.cpp:6:12: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 int i2 {7.2}; ^ $ ./a.out 7 7 7 

Inoltre, l’avviso è solo per il secondo incarico, ma non vi è alcun avviso per il terzo compito. Questo sembra indicare che il = non sia realmente ridondante come menzionato nel libro. Se = fosse ridondante, sia il secondo che il terzo incarico avrebbero prodotto avvertimenti o entrambi non avrebbero prodotto avvertimenti. Quindi li compilo con il -std=c++11 .

 $ g++ -std=c++11 init.cpp init.cpp: In function 'int main()': init.cpp:6:16: warning: narrowing conversion of '7.2000000000000002e+0' from 'double' to 'int' inside { } [-Wnarrowing] int i2 {7.2}; ^ init.cpp:7:18: warning: narrowing conversion of '7.2000000000000002e+0' from 'double' to 'int' inside { } [-Wnarrowing] int i3 = {7.2}; ^ $ ./a.out 7 7 7 

Ancora nessun errore. Solo avvertenze. Sebbene in questo caso il secondo e il terzo compito si comportino in modo identico rispetto alla generazione di avvertimenti.

Quindi la mia domanda è: sebbene il libro accenni che il secondo e il terzo compito sono errori, perché questo codice non riesce a compilare?

Questo è mal formato e dovrebbe essere diagnosticato, tuttavia può essere un avvertimento ( che hai ricevuto ) o un errore. gcc ha fatto di questo un avvertimento per diverse versioni a causa del problema di porting di C ++ 03 :

Lo standard richiede solo che “un’implementazione conforms emetta almeno un messaggio diagnostico”, pertanto è consentita la compilazione del programma con un avviso. Come diceva Andrew, -Werror = restringimento ti permette di commettere un errore se vuoi.

G ++ 4.6 ha dato un errore ma è stato modificato intenzionalmente per 4.7 perché molte persone (me compreso) hanno scoperto che il restringimento delle conversioni era uno dei problemi più comuni quando si cercava di compilare grandi codebase C ++ 03 come C ++ 11. Codice precedentemente ben formato come char c [] = {i, 0}; (dove sarò sempre nel raggio di char) ha causato errori e dovevo essere cambiato in char c [] = {(char) i, 0}

ma ora le versioni recenti di gcc e clang rendono questo un errore, vederlo dal vivo per gcc .

Per riferimento, la bozza di standard C ++ 11 sezione 8.5.4 [dcl.init.list] dice:

Altrimenti, se l’elenco di inizializzazione ha un singolo elemento, l’object o il riferimento viene inizializzato da quell’elemento; se una conversione restringente (vedi sotto) è necessaria per convertire l’elemento in T, il programma è mal formato. [ Esempio:

 int x1 {2}; // OK int x2 {2.0}; // error: narrowing 

-End esempio]

e:

Una conversione restringimento è una conversione implicita

  • da un tipo a virgola mobile a un tipo intero o

[…]

[Nota: come indicato sopra, tali conversioni non sono consentite al livello più alto nelle inizializzazioni di lista. -Nota finale] [Esempio:

[…]

 int ii = {2.0}; // error: narrows 

[…]

Quindi un punto fluttuante per la conversione di interi è una conversione restringente ed è mal formato.

e sezione 1.4 Conformità all’implementazione [intro.compliance] dice:

Sebbene questo standard internazionale imponga solo requisiti sulle implementazioni C ++, tali requisiti sono spesso più facili da comprendere se sono formulati come requisiti su programmi, parti di programmi o esecuzione di programmi. Tali requisiti hanno il seguente significato:

[…]

  • Se un programma contiene una violazione di qualsiasi regola diagnosticabile o un’occorrenza di un costrutto descritto in questo Standard come “supportato in modo condizionale” quando l’implementazione non supporta tale costrutto, un’implementazione conforms deve emettere almeno un messaggio diagnostico.

[…]

Ci dice che è richiesta solo una diagnosi.

Il linguaggio C ++ non distingue gli “avvertimenti” da “errori”. C ++ ha solo messaggi diagnostici . Gli avvisi ricevuti sono messaggi diagnostici. Le specifiche del linguaggio non richiedono che i compilatori interrompano la compilazione quando incontrano codice erroneo (ovvero mal formato). Tutti i compilatori devono fare un messaggio diagnostico e quindi possono continuare a compilare, se lo desiderano.

Ciò significa che in generale è responsabilità dell’utente segnalare allarmi che sono “solo avvertimenti” di avvertenze che indicano effettivamente errori veri, in particolare con compilatori permissivi come GCC.

Ciò significa anche che il comportamento reale della vita reale dipende dall’impostazione del compilatore. Chiedi al tuo compilatore di essere più restrittivo al riguardo, se ansible. In GCC si potrebbe provare a -pedantic-errors a tale scopo.

PS Nei miei esperimenti con GCC, -std=c++11 è sufficiente per generare errori per il tuo codice. Se ricevi degli avvisi, potrebbe essere una questione di versione del compilatore.