23

Considerate questo pezzo di codice C++ 11:Restringendo conversione a bool nella lista-inizializzazione - strano comportamento

#include <iostream> 

struct X 
{ 
    X(bool arg) { std::cout << arg << '\n'; } 
}; 

int main() 
{ 
    double d = 7.0; 
    X x{d}; 
} 

C'è una conversione restringimento da un doppio a un bool nella inizializzazione del x. Secondo la mia comprensione dello standard, questo è un codice mal formato e dovremmo vedere qualche diagnostica.

Visual C++ 2013 genera un errore:

error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion 

Tuttavia, entrambi i Clang 3.5.0 e GCC 4.9.1, utilizzando le seguenti opzioni

-Wall -Wextra -std=c++11 -pedantic 

compilare questo codice con senza errori e nessun avviso. L'esecuzione del programma produce uno 1 (nessuna sorpresa).


Ora, andiamo più in profondità in un territorio strano.

Change X(bool arg)-X(int arg) e, improvvisamente, abbiamo un errore dal Clang

error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing] 

e un avvertimento da GCC

warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing] 

Questo sembra più simile a quello che mi aspettavo.


Ora, tenere l'argomento bool costruttore (cioè, ripristinare X(bool arg)), e cambiare double d = 7.0; a int d = 7;. Di nuovo, un errore di restringimento da Clang, ma GCC non rilascia alcuna diagnostica e compila il codice.

Ci sono alcune varianti di comportamento che possiamo ottenere se passiamo la costante direttamente al costruttore, alcune strane, alcune previste, ma non le elencherò qui - questa domanda sta diventando troppo lunga.


Direi che questo è uno dei rari casi in cui VC++ è giusto e Clang e GCC sono sbagliate quando si tratta di standard di conformità, ma, date le rispettive track record di questi compilatori, sono ancora molto titubante su questo.

Cosa pensano gli esperti?


riferimenti standard (citazioni dal documento standard finale per C++ 11, ISO/IEC 14.882-2011):

In 8.5.4 [dcl.init.list] paragrafo 3, abbiamo avere:

— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

nella stessa sezione, al paragrafo 7, abbiamo:

A narrowing conversion is an implicit conversion
— from a floating-point type to an integer type, or
— from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
— from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
— from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end note ]

in 3.9.1 [di base.fondamentale] comma 7, abbiamo:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

(stavo iniziando a mettere in discussione tutto ciò che in questa fase ...)

+0

Ehi, da dove tutti i commenti vanno? Alcuni di essi contenevano informazioni utili per diagnosticare il problema, in particolare per Clang. – bogdan

+0

Alcuni di questi commenti sarebbero stati molto utili per archiviare i bug report, non capisco perché sono stati tutti cancellati, forse chiedendo su [meta] (http://meta.stackoverflow.com/) può aiutare, io Adesso hai il tempo. Puoi anche provare una bandiera personalizzata, ma non sai per quanto tempo prima verrà applicata. –

+0

@dyp non è sicuro che lo vedrete dato che il vostro commento è stato cancellato ma i vostri utili collegamenti al codice sorgente clang sono stati rimossi e sarebbe utile riaverli. –

risposta

14

Questo appare semplicemente come un insetto, se cerchiamo il seguente:

bool b {3} ; 

sia gcc e clang emettere una diagnosi, per esempio gcc dice:

warning: narrowing conversion of '3' from 'int' to 'bool' inside { } [-Wnarrowing]

Questo argomento è trattato nella draft C++11 standard dalla sezione 8.5.4List-inizializzazione paragrafo che dice:

A narrowing conversion is an implicit conversion

[...]

  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.

Questo è lo stesso paragrafo che copre il vostro esempio e il seguente esempio più semplice:

bool a {3.0} ; 

che sarà coperto da questo punto dal paragrafo 7 citato sopra:

  • from a floating-point type to an integer type, or

al paragrafo 3, questo è mal formato un richiede una diagnosi:

List-initialization of an object or reference of type T is defined as follows:

[...]

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

che gcc non produce alcun diagnostico, ma clang fornisce la seguente avviso, anche se non l'avvertimento di conversione restringimento dovremmo vedere:

warning: implicit conversion from 'double' to 'bool' changes value from 3 to true [-Wliteral-conversion]

nota, sezione 3.9.1[basic.fundamental] dice:

0.123.

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

È necessario presentare una segnalazione di bug sia con clang e gcc.

Jonathan Wakely nota che il compilatore EDG fornisce un errore di restringimento per il codice OPs, che è una forte indicazione che questo dovrebbe effettivamente produrre una diagnostica.

Aggiornamento

ho presentato un bug report gcc e clang.

Il clang bug report has been updated as fixed:

Fixed in r229792.

+0

Sì, l'avvertenza per l'inizializzazione di un 'bool' con una * costante *' doppia' rientra nelle 'poche varianti di comportamento' che ho menzionato nella domanda. Fondamentalmente, in questo caso, Clang continua a non rilevare che si tratta di una restrizione della conversione in inizializzazione della lista, ma ricade nello stesso avviso che emette per ogni inizializzazione di questo tipo con un valore costante (emetterà lo stesso avviso se si sostituisce le parentesi graffe con parentesi, rendendo questa una conversione implicita consentita). – bogdan