Interesuje mnie idea C ++ - const
nie taka konkretna realizacja (jak rzutowanie const
).
Weźmy na przykład C # - brakuje C ++ - podobnie jak const, a przyczyną tego są zwykli ludzie i czas. Tutaj dodatkowo wydaje się, że zespół C # spojrzał na wykonanie C ++ const
, marketing CLR i miał w tym momencie dość (zobacz dlaczego nie ma metody const member w parametrze c # i const ; dziękuję Svick). Czy jeśli pójdziemy dalej, czy jest coś jeszcze?
Czy jest coś głębszego? Weźmy na przykład dziedziczenie wielokrotne - zwykle jest postrzegane jako trudne do uchwycenia (dla użytkownika) i dlatego nie jest dodawane do języka (jak problem z diamentem).
Czy jest coś w NATURY o const
to stanowić problem, że lepiej jest dla języka, aby tego uniknąć? Coś w rodzaju decyzji, czy const
powinien być głęboki czy płytki (posiadanie const
kontenera oznacza, że nie mogę również dodawać nowego elementu ani zmieniać istniejących elementów; co jeśli elementy są typu odniesienia)?
AKTUALIZACJA : chociaż wspominam o C #, a tym samym czyniąc perspektywę historyczną, jestem zainteresowany naturą potencjalnych problemów z const
językiem.
To nie jest wyzwanie dla języków. Zignoruj takie czynniki, jak aktualne trendy lub popularność - interesują mnie tylko kwestie techniczne - dziękuję.
źródło
Odpowiedzi:
Pamiętaj, że jeśli to Ci odpowiada, ale w językach funkcjonalnych, takich jak Standard ML, domyślnie wszystko jest niezmienne. Mutacja jest wspierana przez ogólny
ref
typ erence. Więcint
zmienna jest niezmienna, aref int
zmienna jest zmienny pojemnik naint
s. Zasadniczo zmienne są rzeczywistymi zmiennymi w sensie matematycznym (nieznana, ale stała wartość), aref
s są „zmiennymi” w sensie programowania imperatywnego - komórką pamięci, z której można zapisywać i odczytywać. (Lubię nazywać je przypisywalnymi ).Myślę, że problem
const
jest dwojaki. Po pierwsze, C ++ nie ma funkcji wyrzucania elementów bezużytecznych, co jest konieczne, aby mieć nietrywialne trwałe struktury danych .const
musi być głęboki, aby mieć jakikolwiek sens, jednak posiadanie w pełni niezmiennych wartości w C ++ jest niepraktyczne.Po drugie, w C ++ musisz się zdecydować,
const
a nie zrezygnować. Ale kiedy zapomnisz oconst
czymś, a później to naprawisz, skończysz w sytuacji „zatruć const”, o której mowa w odpowiedzi @ RobY, gdzieconst
zmiana będzie kaskadowo w całym kodzie. Gdybyconst
było to ustawienie domyślne, nie zobaczyłbyś, że aplikujesz zconst
mocą wsteczną. Dodatkowo konieczność dodawaniaconst
wszędzie powoduje znaczny szum w kodzie.Podejrzewam, że późniejsze języki głównego nurtu (np. Java) były silnie ukształtowane przez sukces i sposób myślenia C i C ++. W tym przypadku nawet w przypadku wyrzucania elementów bezużytecznych większość interfejsów API do zbierania języków zakłada zmienne struktury danych. Fakt, że wszystko jest zmienne i niezmienność jest postrzegany jako przypadek narożny, mówi wiele o imperatywnym sposobie myślenia za popularnymi językami.
EDYCJA : Po przemyśleniu komentarza Greenoldmana zrozumiałem, że
const
nie chodzi bezpośrednio o niezmienność danych;const
koduje w typie metody, czy ma ona skutki uboczne dla instancji.Możliwe jest użycie mutacji, aby osiągnąć referencyjnie przejrzyste zachowanie. Załóżmy, że masz funkcję, która wywoływana sukcesywnie zwraca różne wartości - na przykład funkcję, która odczytuje pojedynczy znak
stdin
. Możemy użyć pamięci podręcznej / zapamiętać wyniki tej funkcji, aby uzyskać referencyjnie przejrzysty strumień wartości. Strumień byłby połączoną listą, której węzły wywołują funkcję przy pierwszej próbie odzyskania ich wartości, ale następnie buforują wynik. Więc jeślistdin
constainsHello, world!
, po raz pierwszy spróbować pobrać wartość pierwszego węzła, to będzie czytać jednąchar
i powrotuH
. Następnie będzie nadal wracałH
bez dalszych wezwań do przeczytaniachar
. Podobnie drugi węzeł odczytałbychar
zstdin
przy pierwszej próbie odzyskania jego wartości, tym razem zwracamye
i buforujemy ten wynik.Interesującą rzeczą jest to, że zamieniłeś proces z natury stanowy w obiekt, który na pozór jest bezpaństwowy. Aby to osiągnąć, konieczna była jednak mutacja stanu wewnętrznego obiektu (poprzez buforowanie wyników) - mutacja była łagodnym efektem . Nie da się uczynić naszego,
CharStream
const
nawet jeśli strumień zachowuje się jak niezmienna wartość. Teraz wyobraź sobie, że istniejeStream
interfejs zconst
metodami i oczekują tego wszystkie twoje funkcjeconst Streams
. TwójCharStream
nie może implementować interfejs!( EDYCJA 2: Najwyraźniej istnieje słowo kluczowe C ++
mutable
, które pozwala nam oszukiwać i tworzyćCharStream
const
. Jednak ta luka niszczyconst
gwarancje - teraz naprawdę nie możesz być pewien, że coś nie zmutuje za pomocą swoichconst
metod. Przypuszczam, że to nie tak źle, ponieważ musisz wyraźnie poprosić o lukę, ale nadal całkowicie polegasz na systemie honoru).Po drugie, załóżmy, że masz funkcje wyższego rzędu - możesz przekazać funkcje jako argumenty do innych funkcji.
const
ness jest częścią sygnatury funkcji, więc nie można przekazywaćconst
niefunkcji jako argumentów funkcji, które oczekująconst
funkcji. Ślepe egzekwowanieconst
tutaj doprowadziłoby do utraty ogólności.Wreszcie, manipulowanie
const
obiektem nie gwarantuje, że nie mutuje on jakiegoś zewnętrznego (statycznego lub globalnego) stanu za twoimi plecami, więcconst
gwarancje nie są tak silne, jak się początkowo wydają.Nie jest dla mnie jasne, czy kodowanie obecności lub braku efektów ubocznych w systemie typów jest ogólnie dobrą rzeczą.
źródło
var
na żądanie pozostawiłby tylko jeden problem - głębokość?const
odniesienie do nie-const
obiektu. Mimo to nie sądzę, aby płytka istniała jakikolwiek sensconst
. Chodzi oconst
to, że nie będziesz mógł modyfikować obiektu za pomocą tego odwołania. Nie wolno ci obchodzićconst
, tworząc brakconst
odniesienia, więc dlaczego miałoby być obchodzoneconst
przez mutowanie elementów obiektu?func foo(const String &s)
i to jest sygnał,s
który nie zostanie zmienionyfoo
.null
i dziedziczeniem).Głównym problemem jest to, że programiści zwykle nie używają go wystarczająco, więc kiedy trafią w miejsce, w którym jest to wymagane, lub opiekun później chce naprawić stałą poprawność, występuje ogromny efekt tętnienia. Nadal możesz pisać doskonale niezmienny kod
const
, kompilator po prostu go nie wymusi i trudniej będzie go zoptymalizować. Niektóre osoby wolą, aby ich kompilator nie pomagał. Nie rozumiem tych ludzi, ale jest ich wielu.W językach funkcjonalnych prawie wszystko jest
const
, a bycie zmiennym jest rzadkim wyjątkiem, jeśli w ogóle jest dozwolone. Ma to kilka zalet, takich jak łatwiejsza współbieżność i łatwiejsze rozumowanie na temat stanu współdzielonego, ale wymaga to przyzwyczajenia się, jeśli pochodzisz z języka o zmiennej kulturze. Innymi słowy, brak chęciconst
jest sprawą ludzi, a nie kwestią techniczną.źródło
Wady widzę jako:
„zatrucie const” jest problemem, gdy jedna deklaracja const może zmusić poprzednią lub następną deklarację do użycia const, a to może spowodować, że jej poprzednie kolejne deklaracje użyją const, itp. I jeśli zatrucie const wpłynie do klasy w bibliotece że nie masz kontroli, możesz utknąć w złym miejscu.
to nie jest absolutna gwarancja. Zwykle zdarzają się przypadki, gdy const chroni tylko odwołanie, ale nie jest w stanie ochronić podstawowych wartości z tego czy innego powodu. Nawet w C istnieją przypadki krawędzi, do których const nie może się dostać, co osłabia obietnicę, którą daje const.
ogólnie wydaje się, że sentyment branży odchodzi od silnych gwarancji czasu kompilacji, bez względu na to, jak duży komfort mogą one przynieść tobie i ja. Więc po południu spędzam dotykając 66% mojej bazy kodu z powodu zatrucia const, trochę Pythona guy wybrał 10 doskonale wykonalnych, dobrze zaprojektowanych, dobrze przetestowanych, w pełni funkcjonalnych modułów Pythona. I nawet nie rąbał, kiedy to robił.
Być może więc pytanie brzmi: po co wprowadzać potencjalnie kontrowersyjną cechę, o której nawet niektórzy eksperci są ambiwalentni, kiedy próbują Państwo przyswoić nowy, sztuczny język?
EDYCJA: krótka odpowiedź, nowy język ma strome wzgórze, na które można się wspinać w celu adopcji. To projektanci naprawdę muszą się zastanowić, jakie funkcje są potrzebne, aby uzyskać adopcję. Weźmy na przykład Go. Google trochę brutalnie obciął niektóre z najgłębszych bitew religijnych, dokonując pewnych wyborów w stylu Conana-Barbarzyńcy, i myślę, że z tego, co widziałem, ten język jest lepszy.
źródło
int *
zmienny wskaźnik na zmienną wartość,int const *
zmienny wskaźnik na stałą wartość,int * const
const wskaźnik na zmienną wartość,int const * const
const wskaźnik na stałą wartość.const
), więc takie „powody” uważam za nieistotne (przynajmniej w przypadku tego pytania). Oconst
zatruciu - czy możesz podać przykład? Ponieważ AFAIK jest zaletą, przekazujesz cośconst
i powinno pozostaćconst
.const
problem, ale zmiana typu naprawdę - cokolwiek zrobisz, zawsze będzie problemem. Rozważ podanieRichString
, a następnie uświadomienie sobie, że lepiej przekazaćString
.const
. Zbyt łatwo jest nieconst
robić rzeczy, które powinny być,const
ponieważ musisz zdecydować się na to zamiast z niego zrezygnować.Dodanie
const
do języka wiąże się z pewnymi problemami filozoficznymi . Jeśli zadeklarujeszconst
się klasie, oznacza to, że klasa nie może się zmienić. Ale klasa jest zbiorem zmiennych połączonych z metodami (lub mutatorami). Abstrakcję osiągamy, ukrywając stan klasy za zestawem metod. Jeśli klasa jest zaprojektowana do ukrywania stanu, potrzebujesz mutatorów, aby zmienić ten stan z zewnątrz, a wtedyconst
już nie jest . Można więc zadeklarowaćconst
tylko te klasy, które są niezmienne.const
Wydaje się więc możliwe tylko w scenariuszach, w których klasy (lub typy, które chcesz zadeklarowaćconst
) są trywialne ...Więc w
const
każdym razie potrzebujemy ? Nie sądzę. Myślę, że bez problemu możesz się obejść bezconst
dobrego projektu. Jakich danych potrzebujesz i gdzie, jakie metody są metodami danych do danych i jakich abstrakcji potrzebujesz do I / O, sieci, widoku itp. Następnie naturalnie dzielisz moduły i klasy, które zajmują się stanem i masz metody i niezmienne klasy, które nie potrzebują stanu.Myślę, że większość problemów powstaje w przypadku dużych, grubych obiektów danych, które mogą się zapisywać, przekształcać i rysować, a następnie chcesz je na przykład przekazać do renderera. Nie można więc skopiować zawartości danych, ponieważ jest to zbyt kosztowne dla dużego obiektu danych. Ale nie chcesz, żeby to się zmieniło, więc potrzebujesz czegoś takiego
const
, jak myślisz. Pozwól kompilatorowi odkryć swoje wady projektowe. Jeśli jednak podzielisz te obiekty tylko na niezmienny obiekt danych i zaimplementujesz metody w odpowiedzialnym module, możesz przekazać (tylko) wskaźnik i wykonać czynności, które chcesz zrobić z danymi, jednocześnie gwarantując niezmienność.Wiem, że w C ++ można zadeklarować niektóre metody
const
i nie zadeklarować innych metod, ponieważ są mutatorami. A potem jakiś czas potrzebujesz bufora podręcznego, a następnie getter mutuje obiekt (ponieważ leniwie się ładuje) iconst
już go nie ma. Ale o to właśnie chodzi w tej abstrakcji, prawda? Nie wiesz, jaki stan ulega mutacji przy każdym połączeniu.źródło
const
(w sensie C ++ ) musisz zdefiniować interfejs, także dla wszystkich właściwości.