2015-08-14 17 views
5

I goggled to & próbował znaleźć podobne pytanie na SO również, ale nie znalazłem nic przydatnego. Tak, publikując moje pytanie tutaj.Dlaczego niejawna konwersja Bool na ciąg nie jest błędem?

Rozważmy następujący program:

#include <iostream> 
void foo(const std::string &) {} 
int main() 
{ 
    foo(false); 
} 
 
[Warning] converting 'false' to pointer type for argument 1 of 'std::basic_string::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits; _Alloc = std::allocator]' [-Wconversion-null] 

dlaczego C++ pozwala to bez wyraźnej obsady? Spodziewałem się błędu kompilatora. Program kończy się nienormalnie przy starcie z powodu wyjątku wyświetlania następuje:

terminate called after throwing an instance of 'std::logic_error' 
    what(): basic_string::_S_construct null not valid 

This application has requested the Runtime to terminate it in an unusual way. 
Please contact the application's support team for more information. 

Co średnia mówi o tego typu niejawna konwersja?

+1

"Co standard mówi o tego rodzaju niejawnej konwersji?" Mówi, że jest to dozwolone. – juanchopanza

+0

@juanchopanza: Nie. To nie tak. musisz napisać odpowiedni tekst ze standardu C++. – Destructor

+5

@Downvoters: dlaczego downvotes? Co jest nie tak? – Destructor

Odpowiedz

7

Zanim C++ 11 wprowadziło słowo kluczowe nullptr, wskaźniki zerowe były trochę hackowane. Każda liczba całkowita równa zero będzie wystarczająca jako zerowa stała wskaźnika, a false pasuje do rachunku.

Tak więc efektem twojego programu jest skonstruowanie std::string z argumentem char const * z NULL. Konstruktor nie obsługuje wskaźników null, więc otrzymujesz niezdefiniowane zachowanie.

Rozwiązaniem tego problemu jest użycie nowszego dialektu C++. W razie potrzeby prześlij -std=c++11 do kompilatora lub -std=c++14.Następnie należy uzyskać coś takiego:

error: no matching function for call to 'foo' 

http://coliru.stacked-crooked.com/a/7f3048229a1d0e5a

EDIT: Hmm, GCC nie wydaje się jeszcze wdrożyć tę zmianę. To trochę zaskakujące. Możesz spróbować Clang.

Mam filed raport o błędzie.

+1

GCC nadal kompiluje go z włączonymi C++ 11 lub C++ 14. – Quentin

+3

@Quentin, wydaje diagnozę, która jest technicznie wszystko, co jest wymagane dla źle sformułowanego programu. Wolałbym, żeby był to błąd, a nie ostrzeżenie. –

+2

@ JonathanWakely Ale co z SFINAE i częściowym ładowaniem przeciążenia? Jestem leniwy, aby przetestować konkretny przykład, przepraszam. – Potatoswatter

-1

bool jest zasadniczo liczbą całkowitą, więc będzie on interpretowany jako zero, w Twoim przypadku jest to probablly 0 znaków i to spowoduje, że problemu ciąg będzie wyjątek

3

Co się dzieje, jest to, że std::string jest domyślnie zbudowany z false, przy użyciu const CharT* przeciążenia i konwersji false do wskaźnika pustego. Zgodnie z dokumentacją dla konstruktor:

zachowanie jest nieokreślona, ​​gdy s [wskaźnik] nie jest skierowany na szereg przynajmniej Traits::length(s)+1 elementów CharT.

Stąd nieprawidłowe działanie (w formie przyjaznego wyjątku, ale nie należy na nim polegać).

Czy to prawda? Według [conv.ptr]

NULL stałe jest liczbą całkowitą dosłownym (2.13.2) o wartości zero lub prvalue typu std::nullptr_t.

false ma rzeczywiście wartość zero, ale nie jest liczbą całkowitą (jest to literał boolowski). Niejawna konwersja do konstruktora CharT*, którą przyjmuje std::string, jest zatem niestandardowa.

I rzeczywiście, podczas gdy GCC emituje ostrzeżenie, Clang odmawia skompilowania go.

+2

Clang kompiluje je, jeśli podasz '-std = C++ 03'. To zależy od tego, który dialekt jest domyślny dla konkretnej wersji kompilatora. – Potatoswatter

+0

@Potatoswatter Ale pytanie nie wspomina o C++ 03. W C++ 11 i późniejszych tylko literalne '0' może zostać przekonwertowane na wskaźnik zerowy. – Quentin

+0

@Quentin: dlaczego mówisz, że nie polegasz na formie przyjaznego wyjątku? – Destructor