Dlaczego w C ++ 20 wprowadzono std :: ssize ()?

99

C ++ 20 wprowadził std::ssize()bezpłatną funkcję, jak poniżej:

template <class C>
    constexpr auto ssize(const C& c)
        -> std::common_type_t<std::ptrdiff_t,
                              std::make_signed_t<decltype(c.size())>>;

Wydaje się static_cast, że możliwa implementacja polega na przekształceniu wartości zwracanej size()funkcji składowej klasy C na jej podpisany odpowiednik.

Ponieważ size()funkcja składowa C zawsze zwraca wartości nieujemne, dlaczego ktoś miałby chcieć przechowywać je w podpisanych zmiennych? Na wypadek, gdyby ktoś naprawdę chciał, jest to prosta sprawa static_cast.

Dlaczego std::ssize()wprowadzono w C ++ 20?

John Z. Li
źródło
4
@ Jarod42 Czy to nie jest implementacja zdefiniowana zamiast niezdefiniowana? (przepełnienie ze
znakiem
8
Gdyby tylko dodali również ssizeofoperator.
geza
3
To może być trochę powiązane: stackoverflow.com/questions/30395205/ ...
Marco13
10
@ JohnZ.Li Ryzykując, że zabrzmi to zbyt nieświadomie: myślę, że cały system typów C ++ dotyczący typów całkowitych jest uszkodzony. Jasne, można argumentować, że niektóre dziwactwa (takie jak brak wiedzy, ile bitów charma) są dziedziczone po C i przynajmniej w pewnym stopniu łagodzone przez (u)intX_t, ale wciąż jest to nieskończone źródło równie subtelnych i krytycznych błędów. ssizeTakie rzeczy są tylko łatkami i zajmie trochę czasu (może „na zawsze”), zanim zagłębią się w powszechne „przewodniki po najlepszych praktykach”, których ludzie (mogą) rygorystycznie przestrzegać.
Marco13
6
@ Marco13: Z drugiej strony, układ typu C / C ++ (w przeciwieństwie do stałej instalacji typów np Java), oprócz pozwalając C / C ++ kod do pracy na architekturach gdzie większość innych języków rechotać, ma umożliwić właściwe instruktorzy, aby uzyskać pewne ważne lekcje do głowy ucznia. Na przykład nie cały świat jest 64-bitowy. I nie, nie cały świat używa 8-bitowych znaków. Bardzo łatwo jest sobie z tym radzić i czyni cię lepszym programistą, gdyby tylko instruktorzy uczyli tego od początku . (I, by upewnić się, ty nie wiesz, że (u)intX_ttypy są opcjonalne , prawda?)
DevSolar

Odpowiedzi:

69

Uzasadnienie zostało opisane w tym artykule . Cytat:

Kiedy span został przyjęty do C ++ 17, używał liczby całkowitej ze znakiem zarówno jako indeksu, jak i rozmiaru. Częściowo miało to pozwolić na użycie „-1” jako wartości wartowniczej w celu wskazania typu, którego rozmiar nie był znany w czasie kompilacji. Jednak posiadanie kontenera STL, którego funkcja size () zwracała wartość ze znakiem, było problematyczne, dlatego wprowadzono P1089, aby „naprawić” problem. Otrzymał poparcie większości, ale nie margines 2 do 1 potrzebny do osiągnięcia konsensusu.

Ten dokument, P1227, był propozycją dodania nie-składowych funkcji std :: ssize i składowych ssize (). Włączenie ich uczyniłoby pewien kod znacznie prostszym i pozwoliłoby na uniknięcie niechcianego braku znaku w obliczeniach rozmiaru. Pomysł polegał na tym, że odporność na P1089 zmniejszyłaby się, gdyby funkcja ssize () była dostępna dla wszystkich kontenerów, zarówno za pośrednictwem std :: ssize (), jak i jako funkcje składowe.

Nadav Har'El
źródło
30
for(int i = 0; i < container.ssize() - 1; ++i)Przykładem jest również dość przekonujące
Caleth
7
@John, wydaje mi się, że rzeczywiście mogliby zrobić to samo, co string :: npos i po prostu użyć size_t (-1) jako wartości specjalnej.
rubenvb
15
@ JohnZ.Li Od dawna uważa się za błąd, że typy rozmiaru STL są bez znaku. Teraz, niestety, jest już za późno na reformę. Zapewnienie bezpłatnej funkcji to najlepsze, co możemy teraz zrobić.
LF
16
@LF: To był Herb Sutter na konferencji (może Bjarne też to powiedział). Ale trochę się myli. Teraz, na komputerach 32-bitowych / 64-bitowych, rozmiar ze znakiem byłby lepszy (więc ma rację). Ale w dawnych czasach (rozmiary 16-bitowe) rozmiar ze znakiem byłby zły (na przykład mogliśmy przydzielić tylko 32-bajtowe tablice).
geza
11
@LF: Znalazłem, że Herb wspomina o tym: youtube.com/watch?v=Puio5dly9N8&t=2667 . Kiedy mówi, że „w praktyce niewiele się to zdarza”, jest to prawdą w dzisiejszych czasach. Ale to nie była prawda> 20 lat temu (systemy 16-bitowe). Tak więc nie było wielkim błędem używanie unsigned, kiedy projektowano STL.
geza
50

Nieodpłatnie skradzione Ericowi Nieblerowi:

'Unsigned types signal that a negative index/size is not sane'była dominującą mądrością przy projektowaniu STL. Ale logicznie rzecz biorąc, liczba rzeczy nie musi być pozytywna. Mogę chcieć zachować liczbę w podpisanej liczbie całkowitej, aby oznaczyć liczbę elementów dodanych lub usuniętych z kolekcji. Wtedy chciałbym to połączyć z rozmiarem kolekcji. Jeśli rozmiar kolekcji jest bez znaku, teraz jestem zmuszony mieszać arytmetykę ze znakiem i bez znaku, co jest farmą błędów. Kompilatory ostrzegają przed tym, ale ponieważ projekt STL prawie zmusza programistów do takiej sytuacji, ostrzeżenie jest tak powszechne, że większość ludzi je wyłącza. Szkoda, bo to ukrywa prawdziwe błędy.

Używanie unsigned ints w interfejsach nie jest dobrodziejstwem, które wielu ludzi myśli, że jest. Jeśli przypadkowo użytkownik przekaże do API nieznacznie ujemną liczbę, nagle stanie się ona dużą liczbą dodatnią. Gdyby interfejs API wziął numer jako podpisany, może wykryć sytuację, stwierdzając, że liczba jest większa lub równa zero.

Jeśli ograniczymy użycie niepodpisanych intów do bitowego fałszowania (np. Masek) i użyjemy znaków int ze znakiem wszędzie indziej, prawdopodobieństwo wystąpienia błędów jest mniejsze i łatwiejsze do wykrycia, kiedy się pojawią.

sp2danny
źródło
6
Swift przyjmuje to podejście, nawet jeśli nie ma obaw o to, że ujemne liczby ze znakiem zostaną ponownie zinterpretowane jako ogromne liczby bez znaku (ponieważ nie ma niejawnych rzutów, które naprawdę wprowadzają cię do tego szalonego wesołego domu). Po prostu przyjmują podejście, że (wielkość słowa maszynowego) Intpowinna być typową walutą liczb całkowitych, nawet jeśli tylko liczby dodatnie mają sens (na przykład indeksowanie tablicy). Wszelkie odstępstwa od niej powinny być uzasadnione. Miło jest nie martwić się o rzuty wszędzie.
Alexander - Przywróć Monikę
3
@ JohnZ.Li Rzeczywiście, „unsigned int uważany za szkodliwy dla Javy”
Nayuki