23

Rozważmy następujący fragment kodu C++ 11:Zawężenie konwersja do bool w liście inicjalizacji - dziwne zachowanie

#include <iostream> 

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

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

istnieje konwersja zwężenie z podwójnym do bool w inicjalizacji x. Zgodnie z moim rozumieniem normy, jest to źle sformułowany kod i powinniśmy zobaczyć jakąś diagnostykę.

Visual C++ 2013 wystawia błąd:

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

Jednak oba Clang 3.5.0 i GCC 4.9.1, wykorzystując następujące opcje

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

skompilować ten kod z bez błędów i brak ostrzeżeń. Uruchomienie programu wyprowadza 1 (nic dziwnego).


Teraz przejdźmy głębiej w dziwne terytorium.

Zmień X(bool arg) do X(int arg) i nagle mamy błąd z Clang

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

i ostrzeżenia z GCC

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

wygląda to bardziej jak czego się spodziewałem.


Teraz utrzymać bool konstruktora argumentu (czyli przywrócić X(bool arg)) i zmień double d = 7.0; do int d = 7;. Ponownie, zwężający się błąd z Clang, ale GCC nie wydaje żadnej diagnostyki i kompiluje kod.

Istnieje kilka innych wariantów zachowania, które możemy uzyskać, jeśli przekazujemy stałą bezpośrednio konstruktorowi, niektóre dziwne, inne się tego spodziewają, ale nie będę ich tutaj wymieniać - to pytanie jest zbyt długie, jak jest.


powiedziałbym, jest to jeden z nielicznych przypadków, kiedy VC++ jest słuszne i Clang i GCC są złe, jeśli chodzi o standardowej zgodności, ale, biorąc pod uwagę odpowiednie udokumentowane tych kompilatorów, jestem wciąż bardzo się wahają.

Co sądzą eksperci?


referencje standardowe (cytaty z końcowego dokumentu standardowego dla C++ 11, ISO/IEC 14882-2011):

W 8.5.4 [dcl.init.list] ustęp 3, my posiadają:

— 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.

w tym samym rozdziale, w paragrafie 7, mamy:

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 ]

w 3.9.1 [podstawowy.fundamentalne] pkt 7, mamy:

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.

(zacząłem kwestionować wszystko, co na tym etapie ...)

+0

Hej, gdzie się wszyscy komentarze iść? Niektóre z nich zawierały przydatne informacje do diagnozowania problemu, w szczególności dla Clanga. – bogdan

+0

Niektóre z tych komentarzy byłyby bardzo przydatne przy wypełnianiu zgłoszeń błędów, nie rozumiem, dlaczego zostały one usunięte, być może zapytanie [meta] (http://meta.stackoverflow.com/) może pomóc, teraz nie ma czasu. Możesz również wypróbować niestandardową flagę, ale nie wiesz, jak długo będzie ona działała. –

+0

@dyp nie jestem pewien, że to zobaczysz, ponieważ twój komentarz został usunięty, ale użyteczne linki do kodu źródłowego klang zostały usunięte i byłoby pomocne, aby je odzyskać. –

Odpowiedz

14

To po prostu wygląda na błąd, jeśli staramy się, co następuje:

bool b {3} ; 

zarówno gcc i clang problem diagnostyczny, np gcc mówi:

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

To jest pokryta w draft C++11 standard przez cesarskie 8.5.4List-inicjalizacji ust który mówi:

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.

To samo ustęp, który obejmuje swój przykład i następujące prostszy przykład:

bool a {3.0} ; 

, która byłaby objęta tym punktem od punktu 7 cytowanego powyżej:

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

Z ust 3, to jest źle zawarły wymaga diagnostyki:

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.

który gcc produkuje nie diagnostyczna ale clang robi podać następujące ostrzeżenie, chociaż nie ostrzeżenie konwersja zwężenie powinniśmy zobaczyć:

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

Uwaga, sekcja 3.9.1[basic.fundamental] mówi:

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.

Powinieneś złożyć raport błędu zarówno z clang i gcc.

Jonathan Wakely zauważa, że ​​kompilator EDG podaje błąd zawężający dla kodu OP, co jest silną wskazówką, że rzeczywiście powinno to spowodować diagnozę.

Aktualizacja

Złożyłem raport o błędzie gcc i clang.

clang bug report has been updated as fixed:

Fixed in r229792.

+0

Tak, ostrzeżenie o inicjalizacji 'bool' ze * stałą *' podwójnym' mieści się w "kilku innych wariantach zachowania", o których wspomniałem w pytaniu. Zasadniczo w tym przypadku Clang nadal nie wykrywa, że ​​jest to zwężająca się konwersja w inicjalizacji listy, ale powraca do tego samego ostrzeżenia, które wystawia dla takiej inicjalizacji o stałej wartości (wyda to samo ostrzeżenie, jeśli zastąpi nawiasy klamrowe z nawiasami, dzięki czemu jest dozwolona niejawna konwersja). – bogdan