Dlaczego C++ STL jest tak mocno oparty na szablonach? (a nie na *interfejsach*)

Mam na myśli, poza obowiązującą nazwą (standardową biblioteką szablonów)...

C++ początkowo zamierzał przedstawić koncepcje OOP w C. Oznacza to, że można powiedzieć, co konkretna jednostka może, a czego nie może zrobić (niezależnie od tego, jak to robi) na podstawie swojej klasy i hierarchii klas. Niektóre kompozycje zdolności są trudniejsze do opisania w ten sposób ze względu na problematykę dziedziczenia wielokrotnego, a także fakt, że C++ wspiera koncepcję interfejsów w nieco niezdarny sposób (w porównaniu do Javy itp.), ale jest tam (i można go poprawić).

I wtedy pojawiły się szablony, wraz z STL. STL zdawało się przyjmować klasyczne koncepcje OOP i spłukiwać je w odpływie, używając zamiast tego szablonów.

Należy rozróżnić przypadki, w których szablony są używane do uogólniania typów, w których typy są nieistotne dla działania szablonu(kontenery, przykłady). Posiadanie vector<int> czyni doskonałym sens.

Jednak w wielu innych przypadkach (Iteratory i algorytmy) typy szablonów powinny być zgodne z" koncepcją " (iterator wejściowy, iterator do przodu itp...), gdzie rzeczywiste szczegóły pojęcia są definiowane w całości przez implementację funkcji/klasy szablonu, a nie przez klasę typu używanego z szablonem, co jest nieco przeciwstawne do użycia OOP.

Na przykład możesz podać funkcję:

void MyFunc(ForwardIterator<...> *I);

Update: ponieważ było to niejasne w oryginalne pytanie, ForwardIterator jest ok, aby być szablonem, aby umożliwić dowolny typ ForwardIterator. Przeciwieństwem jest posiadanie Forwarditeratora jako koncepcji.

Oczekuje iteratora Forward tylko patrząc na jego definicję, gdzie trzeba spojrzeć na implementację lub dokumentację dla:

template <typename Type> void MyFunc(Type *I);

Dwa argumenty przemawiające za używaniem szablonów: skompilowany kod może być bardziej wydajny, poprzez dopasowanie szablonu do każdego używanego typu, zamiast używania vtables. Oraz fakt, że szablony mogą być używane z natywnymi typami.

Szukam jednak głębszego powodu, dla którego porzucenie klasycznego OOP na rzecz szablonów dla STL? (Zakładając, że czytasz tak daleko :P)

Author: Quentin, 2009-06-24

13 answers

Krótka odpowiedź brzmi "ponieważ C++ ruszył dalej". Tak, pod koniec lat 70-tych Stroustrup zamierzał stworzyć ulepszony C z możliwościami OOP, ale to było dawno temu. Do czasu standaryzacji języka w 1998 roku, nie był już językiem OOP. Był to język wielowymiarowy. Z pewnością miał pewne wsparcie dla kodu OOP, ale miał również nakładany język szablonów Turinga, pozwalał na metaprogramowanie w czasie kompilacji, a ludzie odkryli ogólne programowanie. Nagle OOP nie wydawał się taki ważny. Nie wtedy, gdy możemy napisać prostszy, bardziej zwięzły i bardziej efektywny kod przy użyciu technik dostępnych za pomocą szablonów i ogólnego programowania.

OOP nie jest świętym graalem. To uroczy pomysł, i to było całkiem ulepszenie w stosunku do języków proceduralnych w latach 70-tych, kiedy został wynaleziony. Ale szczerze mówiąc, to nie wszystko, na co się składa. W wielu przypadkach jest niezdarny i gadatliwy i tak naprawdę nie promuje kodu wielokrotnego użytku lub modułowość.

Dlatego społeczność C++ jest dziś bardziej zainteresowana programowaniem generycznym i dlaczego wszyscy W końcu zaczynają zdawać sobie sprawę, że programowanie funkcyjne jest również całkiem sprytne. OOP sam w sobie nie jest ładnym widokiem.

Spróbuj narysować wykres zależności hipotetycznego STL "OOP-ified". Ile klas musiałoby o sobie wiedzieć? Istnieje lot zależności. Czy mógłbyś dołączyć tylko nagłówek vector, bez wciągnięcia iterator lub nawet iostream? STL ułatwia to. Wektor wie o typie iteratora, który definiuje i to wszystko. Algorytmy STL nie wiedzą nic . Nie muszą nawet zawierać nagłówka iteratora, mimo że wszystkie Iteratory akceptują jako parametry. Która jest bardziej modułowa?

STL może nie przestrzegać zasad OOP, jak definiuje to Java, ale czy nie osiąga celów OOP? Czy nie osiąga wielokrotnego użytku, niskiego sprzężenia, modułowość i hermetyzacja?

I czy nie osiąga tych celów lepiej niż wersja OOP-ified?

Jeśli chodzi o to, dlaczego STL został przyjęty do języka, wydarzyło się kilka rzeczy, które doprowadziły do STL.

Najpierw dodano szablony do C++. Zostały one dodane z tego samego powodu, dla którego generyki zostały dodane do .NET. wydawało się dobrym pomysłem, aby móc pisać rzeczy takie jak" kontenery typu T " bez wyrzucania bezpieczeństwa typu. Oczywiście realizacja osiedlili się na było dość dużo bardziej skomplikowane i potężne.

Potem ludzie odkryli, że mechanizm szablonu, który dodali, był jeszcze potężniejszy niż oczekiwano. I ktoś zaczął eksperymentować z użyciem szablonów, aby napisać bardziej ogólną bibliotekę. Jeden zainspirowany programowaniem funkcyjnym i jeden, który wykorzystywał wszystkie nowe możliwości C++.

Przedstawił go Komitetowi ds. języka C++, któremu zajęło sporo czasu przyzwyczajenie się do niego, ponieważ wyglądał tak dziwnie i inaczej, ale ostatecznie zdałem sobie sprawę, że działa lepiej niż tradycyjne odpowiedniki OOP, które musiałyby zawierać w przeciwnym razie . Wprowadzili więc do niego kilka poprawek i zaadoptowali do biblioteki standardowej.

Nie był to wybór ideologiczny, nie był to wybór polityczny "czy chcemy być OOP, czy nie", ale bardzo pragmatyczny. Ocenili bibliotekę i zobaczyli, że działa bardzo dobrze.

W każdym razie, oba powody, które wymieniasz dla faworyzowania STL są absolutnie niezbędne.

Biblioteka standardowa C++ ma być wydajna. Jeśli jest mniej wydajny niż, powiedzmy, równoważny ręcznie zwijany kod C, to ludzie nie będą go używać. To obniżyłoby produktywność, zwiększyłoby prawdopodobieństwo wystąpienia błędów i ogólnie byłoby złym pomysłem.

I STLma do pracy z typami prymitywnymi, ponieważ typy prymitywne są wszystkim, co masz w C, i są główną częścią obu języków. Jeśli STL nie działa z natywnymi tablicami, to bezużyteczne .

Twoje pytanie ma silne założenie, że OOP jest "najlepszy". Jestem ciekaw dlaczego. Pytasz, dlaczego "porzucili klasyczny OOP". Zastanawiam się, dlaczego powinni się z tym trzymać. Jakie miałyby to zalety?

 615
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 20:09:13

Najbardziej bezpośrednią odpowiedzią na to, o co myślę, że pytasz/narzekasz jest to: założenie, że C++ jest językiem OOP jest fałszywym założeniem.

C++ jest językiem wielowymiarowym. Może być programowany za pomocą zasad OOP, może być programowany proceduralnie, może być programowany ogólnie (szablony), a w C++11 (wcześniej znany jako C++0x) niektóre rzeczy mogą być nawet programowane funkcjonalnie.

Projektanci C++ postrzegają to jako zaletę, więc twierdzą, że ograniczanie C++ do działania jak język czysto OOP, gdy programowanie ogólne rozwiązuje problem lepiej i, cóż, bardziej ogólnie , byłoby krokiem wstecz.

 89
Author: Tyler McHenry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 21:25:54

Rozumiem, że Stroustrup pierwotnie preferował projekt kontenera w stylu OOP i w rzeczywistości nie widział innego sposobu, aby to zrobić. Alexander Stepanov jest odpowiedzialny za STL i jego cele nie obejmowały "uczynienia go zorientowanym obiektowo":

To jest fundamentalny punkt: algorytmy są definiowane na strukturach algebraicznych. Zajęło mi kolejne kilka lat, aby zdać sobie sprawę, że trzeba rozszerzyć pojęcie struktury, dodając wymagania złożoności do aksjomaty regularne. ... Uważam, że teorie iteracyjne są tak samo Centralne dla informatyki, jak teorie pierścieni czy przestrzeni Banacha są centralne dla matematyki. Za każdym razem, gdy patrzyłem na algorytm, starałem się znaleźć strukturę, na której jest on zdefiniowany. Chciałem więc ogólnie opisać algorytmy. To właśnie lubię robić. Mogę spędzić miesiąc pracując nad dobrze znanym algorytmem, próbując znaleźć jego ogólną reprezentację. ...

STL, przynajmniej dla mnie, reprezentuje jedyny sposób programowanie jest możliwe. W rzeczywistości jest zupełnie inny od programowania C++, ponieważ został przedstawiony i nadal jest prezentowany w większości podręczników. Ale, widzisz, nie próbowałem programować w C++, próbowałem znaleźć odpowiedni sposób na radzenie sobie z oprogramowaniem. ...

Miałem wiele fałszywych startów. Na przykład, spędziłem lata próbując znaleźć jakieś zastosowanie dla dziedziczenia i wirtualnych, zanim zrozumiałem, dlaczego ten mechanizm jest zasadniczo wadliwy i nie powinien być używany. Bardzo się cieszę, że nikt nie widział wszystkiego etapy pośrednie-większość z nich była bardzo głupia.

(on wyjaśnia, dlaczego dziedziczenie i wirtualne -- a.k.a. obiekt oriented design "był zasadniczo wadliwy i nie powinien być używany" w dalszej części wywiadu).

Kiedy Stepanov przedstawił swoją bibliotekę Stroustrupowi, Stroustrup i inni przeszli przez herkulesowe wysiłki, aby wprowadzić ją do standardu ISO C++ (ten sam Wywiad): {]}

Wsparcie Bjarne Stroustrupa było kluczowe. Bjarne naprawdę chciał STL w standard i jeśli Bjarne czegoś chce, dostaje to. ... Zmusił mnie nawet do wprowadzenia zmian w STL, których nigdy nie zrobiłbym dla nikogo innego ... jest najbardziej samotną osobą, jaką znam. Załatwia sprawy. Zajęło mu to trochę czasu, zanim zrozumiał, o co chodzi w STL, ale kiedy to zrobił, był gotów to przeforsować. Przyczynił się również do STL, broniąc poglądu, że więcej niż jeden sposób programowania jest ważny - przeciwko bez końca flak i hype przez ponad dekadę, i dążąc do połączenie elastyczności, wydajności, przeciążenia i bezpieczeństwa typu w szablonach, które umożliwiło STL. Chciałbym wyraźnie powiedzieć, że Bjarne jest wybitnym projektantem języka mojego pokolenia.

 79
Author: Max Lybbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Odpowiedź znajduje się w tym wywiadzie ze Stepanowem, autorem STL:

Tak. STL nie jest zorientowany obiektowo. I myśl, że orientacja obiektowa jest prawie tak samo mistyfikacja jak sztuczna Inteligencja. Jeszcze nie widziałem ciekawy kawałek kodu, który przychodzi od tych ludzi.

 26
Author: StackedCrooked,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-05 15:54:20

Dlaczego czysty projekt OOP do struktury danych i biblioteki algorytmów byłby lepszy ?! OOP nie jest rozwiązaniem dla każdej rzeczy.

IMHO, STL to najbardziej elegancka biblioteka jaką kiedykolwiek widziałem:)

Na twoje pytanie,

Nie potrzebujesz polimorfizmu runtime, zaletą stl jest zaimplementowanie biblioteki przy użyciu polimorfizmu statycznego, co oznacza wydajność. Spróbuj napisać ogólny sortowanie lub odległość lub co kiedykolwiek algorytm, który ma zastosowanie do wszystkich kontenerów! twój rodzaj w Java wywoła funkcje, które są dynamiczne poprzez n-poziomy do wykonania!

Potrzebujesz czegoś głupiego, jak boks i Unboxing, aby ukryć paskudne założenia tzw. czystych języków OOP.

Jedyny problem jaki widzę z STL i szablonami w ogóle to straszne komunikaty o błędach. Które zostaną rozwiązane za pomocą pojęć w C++0x.

Porównywanie stl do kolekcji w Javie jest jak porównywanie Taj Mahal do mojego domu:)

 19
Author: AraK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 18:03:51

Typy szablonów powinny być następujące "concept" (Input Iterator, Forward Iterator itp...) gdzie rzeczywista szczegóły pojęcia są zdefiniowane w całości poprzez realizację funkcja/klasa szablonu, a nie przez klasy typu używanego z szablon, który jest nieco anty-wykorzystanie OOP.

Myślę, że źle zrozumiałeś przeznaczenie pojęć przez szablony. Na przykład Iterator Forward jest bardzo dobrze zdefiniowanym pojęciem. Aby znaleźć wyrażenia, które muszą być ważne, aby Klasa była Iteratorem Forward i ich semantyka, w tym złożoność obliczeniowa, patrzysz na standard lub na http://www.sgi.com/tech/stl/ForwardIterator.html (aby zobaczyć wszystko, musisz kliknąć linki do iteratora wejściowego, wyjściowego i trywialnego).

Ten dokument jest doskonale dobrym interfejsem, a" rzeczywiste szczegóły pojęcia " są zdefiniowane właśnie tam. Nie są one definiowane przez implementacje iteratorów Forward, nie są też definiowane przez algorytmy wykorzystujące Iteratory Forward.

Różnice w sposobie obsługi interfejsów pomiędzy STL i Javą są trzykrotne:

1) STL definiuje poprawne wyrażenia przy użyciu obiektu, podczas gdy Java definiuje metody, które muszą być wywołane w obiekcie. Oczywiście poprawnym wyrażeniem może być wywołanie metody (funkcji członka), ale nie musi nim być.

2) interfejsy Java są obiektami runtime, podczas gdy pojęcia STL nie są widoczne w czasie runtime nawet z RTTI.

3) Jeśli nie uda Ci się wprowadzić poprawnych wymaganych poprawnych wyrażeń dla koncepcji STL, otrzymasz nieokreślony błąd kompilacji podczas tworzenia instancji szablonu z typem. Jeśli nie uda Ci się zaimplementować wymaganej metody interfejsu Java, pojawi się określony błąd kompilacji.

Ta trzecia część jest, jeśli lubisz Rodzaj (w czasie kompilacji) "typowania kaczek": interfejsy mogą być niejawne. W Javie interfejsy są nieco jawne: klasa "jest" Iterowalna wtedy i tylko jeśli mówi implementuje Iterable. Kompilator może sprawdzić, czy podpisy jego metod są wszystkie obecne i poprawne, ale semantyka jest nadal ukryta (tzn. są albo udokumentowane, albo nie, ale tylko więcej kodu (testów jednostkowych) może powiedzieć, czy implementacja jest poprawna).

W C++, podobnie jak w Pythonie, zarówno semantyka, jak i składnia są niejawne, chociaż w C++ (i w Pythonie, jeśli otrzymasz preprocesor silnego pisania) otrzymujesz pomoc od kompilatora. Jeśli a programista wymaga jawnej deklaracji interfejsów w stylu Javy przez klasę implementującą, wtedy standardowym podejściem jest użycie cech typu (A wielokrotne dziedziczenie może zapobiegać temu, że jest to zbyt gadatliwe). W porównaniu z Javą brakuje jednego szablonu, który mogę utworzyć z moim typem i który skompiluje się wtedy i tylko wtedy, gdy wszystkie wymagane wyrażenia są ważne dla mojego typu. To mi powie, czy zaimplementowałem wszystkie wymagane bity, "zanim go użyję". To wygoda., ale to nie jest rdzeń OOP (i nadal nie testuje semantyki, a kod do testowania semantyki naturalnie testowałby również ważność danych wyrażeń).

STL może lub nie być wystarczająco OO jak na twój gust, ale na pewno oddziela interfejs od implementacji. Nie posiada zdolności Javy do refleksji nad interfejsami i w różny sposób zgłasza naruszenia wymagań interfejsu.

Możesz określić funkcję ... oczekuje iteratora Forward tylko przez patrząc na jego definicję, gdzie trzeba by spojrzeć na realizacji lub dokumentacji do ...

Osobiście uważam, że typy ukryte są siłą, gdy są odpowiednio używane. Algorytm mówi, co robi ze swoimi parametrami szablonu, a wykonawca upewnia się, że te rzeczy działają: to jest dokładnie wspólny mianownik tego, co powinny robić "interfejsy". Ponadto z STL, jest mało prawdopodobne, aby używać, powiedzmy, std::copy w oparciu o znalezienie jego przodu deklaracja w pliku nagłówkowym. Programiści powinni ustalać, co funkcja przyjmuje na podstawie dokumentacji, a nie tylko na podstawie podpisu funkcji. Jest to prawdą w C++, Pythonie lub Javie. Istnieją ograniczenia co do tego, co można osiągnąć pisząc w dowolnym języku, a próba użycia pisania do zrobienia czegoś, czego nie robi (sprawdź semantykę) byłaby błędem.

To powiedziawszy, algorytmy STL zwykle nazywają swoje parametry szablonu w sposób, który wyjaśnia, jaka koncepcja jest wymagana. Ma to jednak na celu dostarczenie użytecznych dodatkowych informacji w pierwszej linii dokumentacji, a nie przekazywanie oświadczeń bardziej informacyjnych. Jest więcej rzeczy, które musisz wiedzieć, niż można zamknąć w typach parametrów, więc musisz przeczytać dokumenty. (Na przykład w algorytmach, które przyjmują zakres wejściowy i iterator wyjściowy, istnieje szansa, że iterator wyjściowy potrzebuje wystarczającej "przestrzeni" dla określonej liczby wyjść w oparciu o rozmiar zakresu wejściowego i być może wartości w nim zawarte. Spróbuj to mocno wpisać.)

Oto Bjarne o interfejsach jawnie deklarowanych: http://www.artima.com/cppsource/cpp0xP.html

W generykach argument musi być Klasa wywodząca się z interfejsu (the Odpowiednikiem interfejsu w języku C++ jest klasa abstrakcyjna) określona w definicja rodzaju. To znaczy że wszystkie ogólne typy argumentów muszą dopasować się do hierarchii. To narzuca niepotrzebne ograniczenia w projektach wymaga nieuzasadnionej dalekowzroczności na na część deweloperów. Na przykład, jeśli piszesz rodzajnik, a ja definiuję Klasa, ludzie nie mogą używać mojej klasy jako argument do twojego generyka chyba, że wiem o interfejsie, który podałeś i z tego czerpałem moją klasę. To sztywny.

Patrząc na to odwrotnie, dzięki pisaniu kaczką możesz zaimplementować interfejs nie wiedząc, że interfejs istnieje. Lub ktoś może napisać interfejs celowo tak, aby Twoja klasa go implementowała, po konsultacji z Twoim docs, aby upewnić się, że nie proszą o coś, czego już nie robisz. To jest elastyczne.

 11
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 20:01:53

" OOP dla mnie oznacza tylko wiadomości, lokalne przechowywanie i ochronę oraz ukrywanie procesu stanowego i ekstremalnie późnego wiązania wszystkich rzeczy. Można to zrobić w Smalltalku i w Lispie. Możliwe, że są inne systemy, w których jest to możliwe, ale nie jestem ich świadomy."- Alan Kay, twórca Smalltalk.

C++, Java i większość innych języków są dość daleko od klasycznego OOP. To powiedziawszy, argumentowanie za ideologiami nie jest zbyt produktywne. C++ nie jest czysty w żadnym sensie, więc to implementuje funkcjonalność, która wydaje się mieć wtedy sens pragmatyczny.

 8
Author: Ben Hughes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 18:01:14

STL rozpoczął się z zamiarem zapewnienia dużej biblioteki obejmującej najczęściej używany algorytm -- z celem consitent zachowania i Wydajność. Szablon był kluczowym czynnikiem umożliwiającym wdrożenie i realizację tego celu.

Aby podać kolejne odniesienie:

[[0]}Al Stevens rozmawia z Alexem Stepanovem, w marcu 1995 roku z DDJ:

Stepanow wyjaśnił swoją pracę doświadczenie i wybór w kierunku dużej biblioteki algorytmów, która ostatecznie przekształciła się w STL.

Powiedz nam coś o swoim długoterminowym zainteresowaniu programowaniem generycznym

.....Potem zaproponowano mi pracę w Bell Laboratories pracując w grupie C++ nad bibliotekami C++. Pytali mnie, Czy Mogę to zrobić w C++. Oczywiście nie znałem C++ i oczywiście powiedziałem, że mogę. Ale nie mogłem tego zrobić w C++, bo w 1987 roku C++ nie miało szablonów, które są niezbędne za włączenie tego stylu programowania. Dziedziczenie było jedynym mechanizmem do uzyskania generyczności i nie było wystarczające.

Nawet teraz dziedziczenie C++ nie jest zbyt przydatne w programowaniu generycznym. Porozmawiajmy dlaczego. Wiele osób próbowało użyć dziedziczenia do implementacji struktur danych i klas kontenerów. Jak teraz wiemy, było niewiele, jeśli żadnych udanych prób. Dziedziczenie C++ i styl programowania z nim związany są dramatycznie ograniczone. Niemożliwe jest wdrożenie projekt, który zawiera tak trywialną rzecz, jak równość korzystania z niego. Jeśli zaczniesz od podstawowej klasy X w korzeniu swojej hierarchii i zdefiniujesz wirtualny operator równości na tej klasie, który przyjmuje argument typu X, wywołaj klasę Y z klasy X. jaki jest interfejs równości? Ma równość, która porównuje Y Z X. używając zwierząt jako przykładu (ludzie kochają zwierzęta), definiują ssaki i wywodzą żyrafy od ssaków. Następnie zdefiniuj funkcję member mate, gdzie zwierzę kojarzy się ze zwierzęciem i zwraca zwierzę. Następnie czerpie się żyrafa ze zwierzęcia i, oczywiście, ma funkcję mate, gdzie żyrafa kojarzy się ze zwierzęciem i zwraca zwierzę. Zdecydowanie nie tego chcesz. Chociaż kojarzenie może nie być bardzo ważne dla programistów C++, równość jest. Nie znam jednego algorytmu, w którym nie używa się równości.

 8
Author: yowkee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Podstawowy problem z

void MyFunc(ForwardIterator *I);

Czy jak bezpiecznie uzyskać Typ rzeczy, którą zwraca iterator? Dzięki szablonom odbywa się to za Ciebie w czasie kompilacji.

 5
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 17:52:04

Pomyślmy przez chwilę o bibliotece standardowej jako bazie danych zbiorów i algorytmów.

Jeśli studiowałeś historię baz danych, niewątpliwie wiesz, że na początku bazy danych były w większości "hierarchiczne". Hierarchiczne bazy danych bardzo ściśle odpowiadały klasycznemu OOP-w szczególności odmianie jednowątkowej, takiej jak używana przez Smalltalk.

Z czasem okazało się, że hierarchiczne bazy danych mogą być wykorzystywane do modelowania niemal cokolwiek, ale w niektórych przypadkach model pojedynczego dziedziczenia był dość ograniczony. Jeśli masz drewniane drzwi, warto było spojrzeć na nie albo jako drzwi, albo jako kawałek jakiegoś surowca (stali, drewna itp.)

Wynaleźli więc bazy danych modeli sieciowych. Bazy danych modeli sieci bardzo ściśle odpowiadają dziedziczeniu wielokrotnemu. C++ obsługuje całkowicie wiele dziedziczeń, podczas gdy Java obsługuje ograniczoną formę (można dziedziczyć tylko z jednej klasy, ale można również zaimplementować jako wiele interfejsów, jak chcesz).

Zarówno bazy danych modeli hierarchicznych, jak i baz danych modeli sieciowych w większości zniknęły z ogólnego przeznaczenia (choć kilka pozostaje w dość specyficznych niszach). W większości przypadków zostały zastąpione relacyjnymi bazami danych.

Głównym powodem przejęcia relacyjnych baz danych była wszechstronność. Model relacyjny jest funkcjonalnie supersetem modelu sieciowego (który z kolei jest supersetem modelu hierarchicznego).

C++ w dużej mierze podążał za ta sama ścieżka. Zgodność pomiędzy dziedziczeniem pojedynczym a modelem hierarchicznym oraz między dziedziczeniem wielokrotnym a modelem sieciowym jest dość oczywista. Korespondencja między szablonami C++ a modelem hierarchicznym może być mniej oczywista, ale i tak jest całkiem blisko.

Nie widziałem formalnego dowodu na to, ale wierzę, że możliwości szablonów są supersetem tych, które są dostarczane przez wielokrotne dziedziczenie (co jest oczywiście supersetem pojedynczej inerhitancji). The one trudne jest to, że szablony są w większości statycznie powiązane-to znaczy, wszystkie wiązania mają miejsce w czasie kompilacji, a nie w czasie uruchamiania. Jako taki, formalny dowód, że dziedziczenie zapewnia superset możliwości dziedziczenia, może być nieco trudny i złożony (lub może być nawet niemożliwy).

W każdym razie myślę, że to jest większość prawdziwego powodu, dla którego C++ nie używa dziedziczenia dla swoich kontenerów-nie ma prawdziwego powodu, aby to zrobić, ponieważ dziedziczenie zapewnia tylko podzbiór możliwości dostarczone przez szablony. Ponieważ szablony są w zasadzie koniecznością w niektórych przypadkach, równie dobrze mogą być używane prawie wszędzie.

 2
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 21:52:33

Jak zrobić porównanie z ForwardIterator*'s? To znaczy, Jak sprawdzić, czy przedmiot, który masz, jest tym, czego szukasz, czy go mijałeś?

Przez większość czasu używałbym czegoś takiego:

void MyFunc(ForwardIterator<MyType>& i)

Co oznacza, że wiem, że wskazuję na MyType i wiem, jak je porównać. Chociaż wygląda jak szablon, tak naprawdę nie jest (bez słowa kluczowego "szablon").

 0
Author: Tanktalus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-24 17:53:11

To pytanie ma wiele świetnych odpowiedzi. Należy również wspomnieć, że szablony obsługują otwarty projekt. Przy obecnym stanie zorientowanych obiektowo języków programowania, przy rozwiązywaniu takich problemów należy użyć wzorca odwiedzającego, a true OOP powinno obsługiwać wiele dynamicznych wiązań. Zobacz Open Multi-Methods for C++, P. Pirkelbauer, et.al. za bardzo interesującą lekturę.

Innym ciekawym punktem szablonów jest to, że mogą być używane na runtime polimorfizm też. Na przykład

template<class Value,class T>
Value euler_fwd(size_t N,double t_0,double t_end,Value y_0,const T& func)
    {
    auto dt=(t_end-t_0)/N;
    for(size_t k=0;k<N;++k)
        {y_0+=func(t_0 + k*dt,y_0)*dt;}
    return y_0;
    }

Zauważ, że ta funkcja będzie również działać, jeśli Value jest pewnego rodzaju wektorem (, a nie std:: vector, który powinien być wywołany std::dynamic_array, aby uniknąć nieporozumień)

Jeśli func jest mała, funkcja Ta wiele zyska na inliningu. Przykładowe użycie

auto result=euler_fwd(10000,0.0,1.0,1.0,[](double x,double y)
    {return y;});

W tym przypadku powinieneś znać dokładną odpowiedź (2.718...), ale łatwo jest skonstruować prostą odę bez rozwiązania elementarnego (Wskazówka: Użyj wielomianu w y).

Teraz, masz duże wyrażenie w func i używasz ODE solver w wielu miejscach, więc Twój plik wykonywalny jest wszędzie zanieczyszczany instancjami szablonów. Co robić? Pierwszą rzeczą, którą należy zauważyć, jest to, że zwykły wskaźnik funkcji działa. Następnie chcesz dodać currying, aby napisać interfejs i jawną instancję

class OdeFunction
    {
    public:
        virtual double operator()(double t,double y) const=0;
    };

template
double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction& func);

Ale powyższa instancja działa tylko dla double , dlaczego nie napisać interfejsu jako szablonu:

template<class Value=double>
class OdeFunction
    {
    public:
        virtual Value operator()(double t,const Value& y) const=0;
    };

I specjalizują się w jakiejś wspólnej wartości rodzaje:

template double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction<double>& func);

template vec4_t<double> euler_fwd(size_t N,double t_0,double t_end,vec4_t<double> y_0,const OdeFunction< vec4_t<double> >& func); // (Native AVX vector with four components)

template vec8_t<float> euler_fwd(size_t N,double t_0,double t_end,vec8_t<float> y_0,const OdeFunction< vec8_t<float> >& func); // (Native AVX vector with 8 components)

template Vector<double> euler_fwd(size_t N,double t_0,double t_end,Vector<double> y_0,const OdeFunction< Vector<double> >& func); // (A N-dimensional real vector, *not* `std::vector`, see above)

Gdyby funkcja była najpierw zaprojektowana wokół interfejsu, wtedy zostałbyś zmuszony do dziedziczenia z tego ABC. Teraz masz tę opcję, a także wskaźnik funkcji, lambda lub dowolny inny obiekt funkcji. Kluczem jest to, że musimy mieć operator()() i musimy być w stanie użyć niektórych operatorów arytmetycznych na jego typie zwrotnym. Tak więc, maszyna szablonów złamałaby się w tym przypadku, gdyby C++ nie miał przeciążenia operatora.

 0
Author: user877329,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 15:04:24

Koncepcja oddzielania interfejsu od interfejsu i możliwości zamiany implementacji nie jest nieodłączna dla programowania obiektowego. Uważam, że jest to pomysł, który został wykluczony w rozwoju opartym na komponentach, takich jak Microsoft COM. (Zobacz Moja odpowiedź na czym polega Component-Driven Development?) Dorastając i ucząc się C++, ludzie byli wychowani na dziedziczenie i polimorfizm. Dopiero w latach 90-tych ludzie zaczęli mówić "Program do 'interfejsu', a nie 'implementacji '" i " Favor "skład obiektu" nad "dziedziczeniem klas"."(oba cytowane przy okazji z GoF).

Potem pojawiła się Java wraz z wbudowanym garbage collector i interface słowem kluczowym, i nagle stało się praktyczne, aby faktycznie oddzielić interfejs i implementację. Zanim się zorientujesz, pomysł stał się częścią OO. C++, Szablony i STL.

 -1
Author: Eugene Yokota,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:23