2014-10-27 5 views

Odpowiedz

27

C++ 14 kod ten jest źle tworzy ale przed C++ 14 kod dobrze uformowane (ale wynik jest nieoznaczony), a defect report 583: Relational pointer comparisons against the null pointer constant Uwagi:

W C, jest źle sformułowany (porównaj C99 6.5.8):

void f(char* s) { 
    if (s < 0) { } 
} 

... ale w C++ nie jest. Czemu? Kto mógłby kiedykolwiek napisać (s> 0) , kiedy mogliby równie dobrze napisać (s! = 0)?

To było w języku od ARM (i prawdopodobnie wcześniej); widocznie dzieje się tak dlatego, że konwersje wskaźnika (4.10 [conv.ptr]) wymagają do wykonania na obu operandach, gdy jeden z argumentów jest typu wskaźnika o numerze . Wygląda więc na to, że konwersja "null-ptr-to-real-pointer-type" łączy się z innymi konwersjami wskaźnika.

C++ 14 przerabia się źle tworzy przy N3624 był applied to the draft C++14 standard, który jest zmiana N3478. Proponowana uchwała o 583 notach:

Ten problem został rozwiązany uchwałą numerze 1512.

emisyjna 1512 proponowanego rozdzielczość to N3478 (N3624 jest rewizja N3478):

Proponowane brzmienie znajduje się w dokumencie N3478.

Zmiany w rozdziale 5.9 z C++ C++ 11 do 14

sekcji 5.9relacyjne operatorzy zmieniło się wiele między C++11 draft standard i C++14 draft standard, następujące podkreśla najistotniejsze różnice (podkr przyszłości), z ust 1:

argumentów Shal Mam typ arytmetyczny, wyliczeniowy lub wskaźnikowy, lub typ std :: nullptr_t.

zmiany:

operandów ma arytmetyczne, wyliczanie lub typ wskaźnika

Więc typ std::nullptr_t nie jest już ważny argument operacji, ale to wciąż pozostawia 0 który jest zerowy wskaźnik o stałej wartości i dlatego można go przekonwertować (sekcja 4.10) na po rodzaj interakcji.

ta jest objęta ust 2 które w C++ 11 mówi:

[...] konwersje Pointer (4,10) i kwalifikacji konwersje (4,4) są wykonywane na argumentach wskazówka (lub na wskaźniku wskaźnika i zerowej stałej wskaźnika lub na dwóch zerowych stałych wskaźnikowych, co najmniej jeden spośród , który jest nieintegralny) w celu dostosowania ich do ich wskaźnika złożonego wskaźnika. Jeśli jeden operand jest zerową stałą wskaźnika, wskaźnik kompozytowy typu to std :: nullptr_t, jeśli drugi operand jest również stałą wskaźnika zerowego lub, jeśli drugi operand jest wskaźnikiem, typ drugiego argumentu . ...]

to wyraźnie przewiduje wyjątek dla zerowy wskaźnik stałej argumentu, zmiany następujące w C++ 14:

zwykłe konwersje arytmetyczne wykonywane są na operandy typ arytmetyczny lub wyliczeniowy. Jeśli oba operandy są wskaźnikami, konwersje wskaźników konwersji (4.10) i kwalifikacji (4.4) są wykonywane w celu dostosowania ich do ich typu wskaźnika złożonego (Klauzula 5). Po przekształceniach argumenty muszą mieć ten sam typ.

, w którym nie ma tak, że umożliwia 0 być przekształcany do rodzaju wskaźnik. Oba operandy muszą być wskaźnikami, aby zastosować konwersje wskaźnika i wymagane jest, aby operandy miały ten sam typ po konwersji. Które nie jest spełnione w przypadku, gdy jeden operand jest wskaźnikiem typu, a drugi jest stałą wskaźnika zerowego 0.

Co jeśli oba operandy są wskaźnikami, ale jeden jest wartością pustego wskaźnika?

R Sahu pyta jest następujący kod dobrze uformowane ?:

char* p = ""; 
char* q = nullptr; 
if (p > q) {} 

tak, C++ 14 kod jest także utworzone zarówno p i q są wskaźnikami ale wynik porównania jest nieokreślone.Zdefiniowane porównania dla dwóch wskaźników określono w ust 3 i mówi:

Porównując wskaźniki do obiektów jest zdefiniowany następująco:

  • Jeśli dwa wskaźniki wskazują na różne elementy tej samej tablicy, albo do jego podobieństw, wskaźnik do elementu z wyższym indeksem porównuje większe.

  • Jeśli jeden wskaźnik wskazuje na element tablicy lub na jego podobiekt, a inny wskaźnik wskazuje jeden za ostatnim elementem tablicy , drugi wskaźnik porównuje większe.

  • Jeśli dwa wskaźniki wskazują na różne non-statycznych danych tego samego przedmiotu lub podobiektów takich członków, rekurencyjnie, wskaźnik do późniejszego deklarowanej członka porównuje większa pod warunkiem, że dwa członkowie mają taką samą kontrolę dostępu (Klauzula 11) i pod warunkiem, że ich klasa nie jest związkiem.

Null wartości pointers nie są zdefiniowane tutaj, a później w ust 4 mówi:

[...] W przeciwnym razie, wynik każdego z operatorów jest nieokreślona.

W C++ 11 to specjalnie sprawia wyników nieokreślonych w ust 3:

Jeśli dwa wskaźniki piq od tego samego punktu typu do różnych obiektów , które nie są członkami tego samego obiektu lub elementy tablicy samego lub do różnych funkcji, albo jeżeli tylko jeden z nich jest zerowa, wyniki p < q, p, q > < p = q i p > = Q są określone.

+1

Czy to oznacza 'char * p =" "; char * q = nullptr; jeśli (p> q) {} 'jest nadal w porządku? –

+0

@RSahu Moje czytanie akapitów "3" i "4", których nie zacytuję z projektu standardu [N3936] (https://github.com/cplusplus/draft/blob/b7b8ed08ba4c111ad03e13e8524a1b746cb74ec6/papers/N3936.pdf) jest że wynik tego porównania jest nieokreślony, ale dobrze uformowany, ponieważ 'p' i' q' są zarówno wskaźnikami. –

+0

Ten akapit 3 jest dość dziwny. W rzeczywistości praktycznej, jeśli implementacja musi sprawić, aby porównania działały, jeśli żaden z nich lub oba nie są puste i jeśli są członkami tego samego obiektu lub elementów z tej samej tablicy, to wydaje się, że byłoby to dość trudne, aby wyniki * nie były * być przewidywalne i logiczne, jeśli warunki * nie są spełnione. To bardzo mylący i hiper-specyficzny zestaw ograniczeń dla mnie. (Chociaż z pewnością jest to tradycja C++ polegająca na dodawaniu wyjątków do wyjątków od reguł i pasm do pomocy zespołu do pomocy zespołu, tak przypuszczam.) –