Dlaczego C++ nie ma odbicia?

To jest nieco dziwaczne pytanie. Moim celem jest zrozumienie decyzji projektowej języka i zidentyfikowanie możliwości refleksji w C++.

  1. Dlaczego Komitet ds. języka C++ nie poszedł w kierunku wdrożenia refleksji w języku? Czy odbicie jest zbyt trudne w języku, który nie działa na maszynie wirtualnej (jak java)?

  2. Gdyby zaimplementować reflection do C++, jakie będą wyzwania?

Chyba zastosowania z refleksji są dobrze znane: edytory mogą być łatwiejsze do napisania, kod programu będzie mniejszy, mocki mogą być generowane dla testów jednostkowych i tak dalej. Ale byłoby świetnie, gdybyś mógł też skomentować użycie refleksji.

Author: amit_grepclub, 2008-12-11

14 answers

Istnieje kilka problemów z odbiciem w C++.

  • To dużo pracy do dodania, a Komitet C++ jest dość konserwatywny i nie spędzaj czasu na radykalnych nowych funkcjach, chyba że są pewni, że się opłaci. (Zaproponowano dodanie systemu modułowego podobnego do. NET assemblies i chociaż myślę, że jest ogólny konsensus, że byłoby miło mieć, to nie jest to ich najwyższy priorytet w tej chwili, i został przesunięty do dobrze po C++0x. The motywacją dla tej funkcji jest pozbycie się #include systemu, ale umożliwiłoby to również przynajmniej niektóre metadane).

  • Nie płacisz za to, czego nie użyj. To jeden z podstawowych elementów filozofie projektowania leżące u podstaw C++. Dlaczego mój kod powinien nosić metadane, jeśli nigdy ich nie potrzebuję? Ponadto dodawanie metadanych może hamować kompilator z optymalizacja. Dlaczego mam za to płacić? koszt w moim kodzie, jeśli nigdy nie będę potrzebował te metadane?

  • Co prowadzi nas do kolejny duży punkt: C++ daje Bardzo kilka gwarancji o skompilowanym kodzie. Na kompilator może robić ładne wszystko, co lubi, tak długo, jak wynikająca z tego funkcjonalność jest tym, co oczekuje się. Na przykład, twój zajęcia nie są zobowiązane do bądź tam . Kompilator może je zoptymalizować, inline wszystko co robią i to często właśnie to robi, ponieważ nawet prosty kod szablonu ma tendencję do stwórz kilka szablonów instancje. Standard C++ biblioteka opiera się na tym agresywnym optymalizacja. Funkcjami są tylko wykonujący, jeśli napowietrzność tworzenie i niszczenie obiekt można zoptymalizować. operator[] na wektorze jest tylko porównywalny z surowym indeksowanie tablicy w wydajności ponieważ cały operator może być inlined i w ten sposób całkowicie usunięte ze skompilowanego kodu. C # i Java złożyć wiele gwarancji na wyjście kompilatora. Jeśli zdefiniuję klasy w C#, wtedy ta klasa będzie exist w wyniku montaż. Nawet jeśli nigdy go nie użyję. Nawet jeśli wszystkie wywołania do swoich funkcji Członkowskich mogą być inlined. Klasa musi być tam, aby refleksja mogła znaleźć to. Część z nich jest łagodzona przez C# kompilacji do bajtowego kodu, co oznacza że kompilator JIT może usunąć definicje klas i inline funkcje, jeśli lubi, nawet jeśli początkowy kompilator C# nie może. w C++, masz tylko jeden kompilator i to musi wypisać wydajny kod. Jeśli zostały dopuszczone do wglądu w metadane pliku wykonywalnego C++ , spodziewałbyś się zobaczyć każdą zdefiniowaną klasę, która oznacza, że kompilator miałby aby zachować wszystkie zdefiniowane klasy, nawet jeśli nie są konieczne.

  • A potem są szablony. Szablony w C++ nie są jak generyki w innych językach. Każdy instancja szablonu tworzy nowy typ. std::vector<int> jest całkowicie oddzielną klasą od std::vector<float>. To sumuje się do wiele różnych typów w całej program. Co powinno nasze odbicie widzisz? Szablon std::vector? Ale jak to możliwe, skoro to konstrukcja kodu źródłowego, która nie ma czyli w czasie pracy? Musiałby zobaczyć poszczególne klasy std::vector<int> oraz std::vector<float>. Oraz std::vector<int>::iterator oraz std::vector<float>::iterator, same dla {[9] } i tak dalej. Oraz po przejściu do szablonu metaprogramowanie, szybko kończy się tworzenie setek szablonów, wszystkie z nich są wcinane i usuwane znowu przez kompilator. Nie mają znaczenie, z wyjątkiem części metaprogram czasu kompilacji. Czy wszystkie te setki klas są widoczne do refleksji? Oni trzeba, bo inaczej nasze odbicie byłoby bezużyteczne, jeśli nawet nie gwarantowałoby to, że klasy, które zdefiniowałem, rzeczywiście będą tam . Pobocznym problemem jest to, że klasa szablonu nie istnieje, dopóki nie zostanie utworzona. Wyobraź sobie program, który używa std::vector<int>. Czy nasz system odbicia powinien widzieć std::vector<int>::iterator? Z jednej strony, można się tego spodziewać. Jest to ważna Klasa, zdefiniowana w kategoriach std::vector<int>, która nie istnieje w metadanych. Z drugiej strony, jeśli program nigdy w rzeczywistości nie używa tego szablonu klasy iteratora, jego typ nigdy nie zostanie utworzony, a więc kompilator nie wygeneruje klasy w pierwszej kolejności. I jest za późno, aby go utworzyć w czasie wykonywania, ponieważ wymaga dostępu do kodu źródłowego.

  • i wreszcie, refleksja nie jest całkiem równie istotne w C++ jak w C#. Na reason is again, template metaprogramowanie. To nie może rozwiązać wszystko, ale w wielu przypadkach, gdy w przeciwnym razie uciekałbyś się do odbicie, można napisać metaprogram, który robi to samo rzecz w czasie kompilacji. boost::type_traits jest prostym przykład. Chcesz wiedzieć o rodzaju T? Sprawdź jego type_traits. W C#, będziesz musiał łowić ryby po jego wpisz za pomocą odbicia. Odbicie przydałoby się jeszcze dla niektórych rzeczy (główne zastosowanie widzę, które metaprogramowanie nie może łatwo replace, jest dla autogenerated kod serializacji), ale byłoby ponieść znaczne koszty dla C++, a nie jest to konieczne tak często jak w innych języki.

Edit: W odpowiedzi na komentarze:

Cdleary: Tak, symbole debugowania robią coś podobnego, ponieważ przechowują metadane o typach używanych w pliku wykonywalnym. Ale cierpią również z powodu problemów, które opisałem. Jeśli kiedykolwiek próbowałeś debugować release build, wiesz, co mam na myśli. Istnieją duże luki logiczne, w których utworzyłeś klasę w kodzie źródłowym, która została wprowadzona w kodzie końcowym. Gdybyś użył odbicia do czegokolwiek przydatne, trzeba by było bardziej niezawodne i spójne. Tak jak jest, typy znikają i znikają prawie za każdym razem, gdy kompilujesz. Zmieniasz drobny szczegół, a kompilator decyduje się zmienić, które typy są inlined, a które nie, jako odpowiedź. Jak wydobyć z tego coś użytecznego, skoro nie masz nawet gwarancji, że Najbardziej odpowiednie typy będą reprezentowane w Twoich metadanych? Typ, którego szukałeś, mógł być tam w ostatniej kompilacji, ale teraz jest zniknął. A jutro ktoś sprawdzi małą niewinną zmianę na małą niewinną funkcję, co sprawia, że typ jest na tyle duży, że nie będzie całkowicie inline, więc wróci ponownie. Nadal jest to przydatne w przypadku debugowania symboli, ale niewiele więcej. Nie chciałbym próbować generować kodu serializacyjnego dla klasy zgodnie z tymi Warunkami.

Oczywiście te problemy mogą zostać rozwiązane. Ale to wraca do mojego punktu # 1. Trzeba by dużo pracy, A C++ Komisja ma wiele rzeczy, które uważają za ważniejsze. Czy korzyść z uzyskania jakiejś ograniczonej refleksji (i byłoby to ograniczone) w C++ jest na tyle duża, aby uzasadnić skupienie się na tym kosztem innych funkcji? Czy istnieje naprawdę ogromna korzyść w dodawaniu funkcji języka core, które można już (głównie) zrobić za pomocą bibliotek i preprocesorów, takich jak QT? Być może, ale potrzeba jest o wiele mniej pilna niż gdyby takie biblioteki nie istniały. Dla konkretnych sugestii, jednak ja uważam, że wyłączenie go na szablonach uczyniłoby go całkowicie bezużytecznym. Na przykład nie można użyć funkcji reflection w bibliotece standardowej. Jakie odbicie nie pozwoli Ci zobaczyć std::vector? Szablony są ogromną częścią C++. Funkcja, która nie działa na szablonach, jest w zasadzie bezużyteczna.

Ale masz rację, można by wprowadzić jakąś formę refleksji. Ale to byłaby poważna zmiana w języku. Tak jak jest teraz, typy są wyłącznie konstruktem w czasie kompilacji. Istnieją na korzyść kompilatora i nic więcej. Po skompilowaniu kodu nie ma żadnych klas. Jeśli się rozciągnąć, można argumentować, że funkcje nadal istnieją, ale tak naprawdę, wszystko jest kilka instrukcji asemblera skoku, i dużo stosu push / pop.nie ma wiele do zrobienia, przy dodawaniu takich metadanych.

Ale tak jak powiedziałem, jest propozycja zmian w modelu kompilacji, dodanie samodzielnych modułów, przechowywanie metadanych dla wybranych typów, pozwalając innym modułom odwoływać się do nich bez bałaganu z #include s. to dobry początek i szczerze mówiąc, dziwię się, że Komitet ds. standardów nie wyrzucił propozycji za zbyt dużą zmianę. Więc może za 5-10 lat? :)

 583
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
2015-10-11 09:42:21

Refleksja wymaga przechowywania pewnych metadanych o typach, które można odpytywać. Ponieważ C++ kompiluje się do natywnego kodu maszynowego i przechodzi ciężkie zmiany ze względu na optymalizację, widok wysokiego poziomu aplikacji jest prawie utracony w procesie kompilacji, w związku z tym nie będzie można ich odpytywać w czasie uruchamiania. Java i. NET używają bardzo wysokiego poziomu reprezentacji w kodzie binarnym dla maszyn wirtualnych, dzięki czemu ten poziom odbicia jest możliwy. W niektórych implementacjach C++ , jednak istnieje coś o nazwie Run Time Type Information (RTTI), które można uznać za pomniejszoną wersję odbicia.

 37
Author: Mehrdad Afshari,
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
2008-12-12 07:02:16

Wszystkie języki nie powinny próbować włączać wszystkich cech każdego innego języka.

C++ jest zasadniczo bardzo, bardzo wyrafinowanym asemblerem makr. Nie jest to (w tradycyjnym sensie) język wysokiego poziomu, taki jak C#, Java, Objective-C, Smalltalk itp.

Dobrze jest mieć różne narzędzia do różnych zadań. Jeśli mamy tylko młotki, wszystko będzie wyglądało jak gwoździe itp. Posiadanie języków skryptowych jest przydatne w niektórych zadaniach, a refleksyjne języki OO (Java, Obj-C, C#) są przydatne dla innej klasy zadań, a super-wydajne języki bliskie maszynom są przydatne dla jeszcze innej klasy zadań (C++, C, Assembler).

C++ wykonuje niesamowitą pracę rozszerzenia technologii asemblera do niesamowitych poziomów złożoności zarządzania i abstrakcji, aby programowanie większe, bardziej złożone zadania znacznie bardziej możliwe dla ludzi. Ale niekoniecznie jest to język, który najlepiej nadaje się dla tych, którzy podchodzą do swojego problemu z perspektywa ściśle wysokiego poziomu (Lisp, Smalltalk, Java, C#). Jeśli potrzebujesz języka z tymi funkcjami, aby jak najlepiej wdrożyć rozwiązanie twoich problemów, podziękuj tym, którzy stworzyli takie języki dla nas wszystkich!

Ale C++ jest dla tych, którzy z jakiegokolwiek powodu muszą mieć silną korelację między ich kodem a działaniem maszyny. Czy to wydajność, czy programowanie sterowników urządzeń, czy interakcja z usługami systemu operacyjnego niższego poziomu, czy cokolwiek innego, C++ lepiej nadaje się do tych zadań.

C#, Java, Objective - C wymagają znacznie większego, bogatszego systemu uruchomieniowego do obsługi ich wykonywania. To środowisko uruchomieniowe musi być dostarczone do danego systemu-preinstalowane, aby wspierać działanie oprogramowania. I ta warstwa musi być utrzymywana dla różnych systemów docelowych, dostosowanych przez inny język, aby działała na tej platformie. I ta środkowa warstwa-ta adaptacyjna warstwa między systemem operacyjnym hosta a Twoim kodem-runtime, jest prawie zawsze pisane w języku takim jak C lub c++ , gdzie wydajność jest #1, gdzie zrozumienie przewidywalnie dokładnej interakcji między oprogramowaniem a sprzętem może być dobrze zrozumiane i manipulowane do maksymalnego zysku.

Uwielbiam Smalltalk, Objective-C i posiadanie bogatego systemu uruchomieniowego z refleksją, metadanymi, zbieraniem śmieci itp. Niesamowity kod można napisać, aby skorzystać z tych udogodnień! Ale to po prostu wyższa warstwa na stosie, warstwa, która musi spoczywać na niższych warstwach, że sami muszą ostatecznie siedzieć na systemie operacyjnym i sprzęcie. I zawsze będziemy potrzebować języka, który najlepiej nadaje się do budowania tej warstwy: C++ / C / Assembler.

Dodatek: C++11/14 kontynuuje rozwijanie zdolności C++ do obsługi abstrakcji i systemów wyższego poziomu. Threading, synchronizacja, precyzyjne modele pamięci, bardziej precyzyjne definicje maszyn abstrakcyjnych pozwalają programistom C++ osiągnąć wiele abstrakcji wysokiego poziomu, że niektóre z tych języków tylko wysokiego poziomu dawniej miał wyłączną domenę, a jednocześnie nadal zapewniał wydajność zbliżoną do metalu i doskonałą przewidywalność (tj. Minimalne podsystemy czasu pracy). Być może w przyszłej rewizji C++, dla tych, którzy tego chcą, zostaną włączone funkcje reflection-a może biblioteka zapewni takie usługi runtime (może jest taka teraz, albo początki takiej w boost?).

 14
Author: Mordachai,
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
2014-11-14 18:20:27

Jeśli naprawdę chcesz zrozumieć decyzje projektowe dotyczące C++, znajdź kopię The Adnotated C++ Reference Manual autorstwa Ellisa i Stroustrupa. Nie jest on aktualny z najnowszym standardem, ale przechodzi przez oryginalny standard i wyjaśnia, jak rzeczy działają i często, jak się w ten sposób.

 11
Author: Michael Kohne,
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
2008-12-11 14:57:57

Reflection może być i było wcześniej zaimplementowane w c++.

Nie jest to natywna funkcja c++, ponieważ ma duży koszt (pamięć i szybkość), który nie powinien być domyślnie ustawiony przez język - język jest" domyślnie maksymalną wydajnością".

Ponieważ nie powinieneś płacić za to, czego nie potrzebujesz, i jak sam mówisz, że jest to potrzebne bardziej w edytorach niż w innych aplikacjach, to powinno być zaimplementowane tylko tam, gdzie tego potrzebujesz, a nie "zmuszane" do wszystkich kod (nie potrzebujesz refleksji nad wszystkimi danymi, z którymi będziesz pracować w edytorze lub innej podobnej aplikacji).

 8
Author: Klaim,
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
2008-12-11 12:58:30

Refleksja dla języków, które ją posiadają, dotyczy tego, ile kodu źródłowego kompilator jest gotów pozostawić w kodzie obiektowym, aby umożliwić refleksję, oraz ile maszyn analitycznych jest dostępnych do interpretacji tych odbitych informacji. O ile kompilator nie zachowa całego kodu źródłowego, reflection będzie ograniczone w możliwości analizy dostępnych faktów na temat kodu źródłowego.

Kompilator C++ nic nie trzyma w pobliżu (cóż, ignorując RTTI), więc nie dostajesz odbicie w języku. (Kompilatory Java i C# przechowują tylko nazwy klas, metod i typów zwracanych, więc otrzymujesz trochę danych odbiciowych, ale nie możesz sprawdzić wyrażeń ani struktury programu, a to oznacza, że nawet w tych językach" z włączoną refleksją " informacje, które możesz uzyskać, są dość rzadkie i w związku z tym naprawdę nie możesz zrobić dużej analizy).

Ale możesz wyjść Poza Język i uzyskać pełne możliwości refleksji. Odpowiedź na inne stack overflow dyskusja na temat refleksji w C omawia to.

 8
Author: Ira Baxter,
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 12:02:48

Powodem, dla którego C++ nie ma odbicia, jest to, że wymagałoby to od kompilatorów dodawania informacji o symbolach do plików obiektowych, takich jak członkowie typu klasy, informacje o członach, o funkcjach i w ogóle. Zasadniczo uczyniłoby to pliki include bezużytecznym, ponieważ informacje przesyłane przez deklaracje byłyby następnie odczytywane z tych plików obiektowych (modułów). W C++ definicja typu może występować wielokrotnie w programie, włączając odpowiednie nagłówki (pod warunkiem że wszystkie te definicje są takie same), więc trzeba by było zdecydować, gdzie umieścić informacje o tym typie, tak jak wymienić tu jedną komplikację. Agresywna optymalizacja wykonywana przez kompilator C++, który może zoptymalizować dziesiątki instancji szablonów klas, jest kolejnym mocnym punktem. Jest to możliwe, ale ponieważ C++ jest kompatybilny z C, stanie się to niezręczną kombinacją.

 6
Author: Johannes Schaub - litb,
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
2008-12-11 13:02:14

Istnieje mnóstwo przypadków użycia reflection w C++, które nie mogą być odpowiednio rozwiązane za pomocą konstrukcji czasu kompilacji, takich jak Meta-programowanie szablonów.

N3340 proponuje rich pointers jako sposób na wprowadzenie refleksji w C++. Między innymi rozwiązuje problem nie płacenia za funkcję, chyba że jej używasz.

 3
Author: pong,
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-08-14 19:04:58

Według Alistaira Cockburna, Podtyp nie może być zagwarantowany w środowisku odblaskowym.

Odbicie jest bardziej istotne dla Systemów typowania utajonego. W C++ wiesz jaki masz typ i wiesz co możesz z nim zrobić.

 2
Author: Nilone,
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-11-06 16:56:12

Odbicie może być opcjonalne, jak dyrektywa preprocesora. Coś jak

#pragma enable reflection

W ten sposób możemy mieć to, co najlepsze z obu światów, bez tej biblioteki pragma byłyby tworzone bez refleksji( bez żadnych kosztów ogólnych, jak to omówiono), wtedy to od indywidualnego dewelopera zależy, czy chcą szybkości, czy łatwości użycia.

 2
Author: user1401491,
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-08-25 13:00:31

Gdyby C++ mogło mieć:

  • Dane członków klasy dla nazw zmiennych, typów zmiennych i modyfikatora const
  • iterator argumentów funkcji (tylko pozycja zamiast nazwy)
  • DANE członka klasy dla nazw funkcji, typu zwracanego i modyfikatora const
  • Lista klas nadrzędnych (w tej samej kolejności jak zdefiniowano)
  • DANE dla członków szablonów i klas nadrzędnych; Rozszerzony szablon (co oznacza, że rzeczywisty typ będzie dostępny dla API reflection, a nie dla "template information of how to get there")

To wystarczyłoby do stworzenia bardzo łatwych w użyciu bibliotek w centrum przetwarzania danych bez typowania, które jest tak powszechne w dzisiejszych aplikacjach internetowych i bazodanowych (wszystkie ORM, mechanizmy przesyłania wiadomości, parsery xml/json, serializacja danych itp.).

Na przykład podstawowe informacje obsługiwane przez makro Q_PROPERTY (część frameworka Qt) http://qt.nokia.com/doc/4.5/properties.html rozszerzony o metody klasy i e) - byłoby niezwykle korzystne dla C++ i dla społeczności programistów w ogóle.

Z pewnością refleksja, o której mówię, nie obejmowałaby znaczenia semantycznego ani bardziej złożonych kwestii (takich jak numery linii kodu źródłowego komentarzy, analiza przepływu danych itp.) - ale nie sądzę, aby były one potrzebne do bycia częścią standardu językowego.

 2
Author: Lightness Races in Orbit,
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
2015-03-13 14:06:58
 1
Author: amit_grepclub,
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
2008-12-11 14:49:22

Refleksja w C++, uważam, że ma kluczowe znaczenie, jeśli C++ ma być używany jako język do dostępu do bazy danych, obsługi sesji internetowych/http i tworzenia GUI. Brak odbicia uniemożliwia ORM (jak Hibernate lub LINQ), parsery XML i JSON, które instancinatują klasy, serializację danych i wiele innych rzeczy (gdzie początkowo dane bez typowania muszą być użyte do utworzenia instancji klasy).

Przełącznik czasu kompilacji Dostępny dla programisty podczas procesu budowania może być używane aby wyeliminować ten problem "płacisz za to, co używasz".

I firmwaredeveloper nie potrzebuje odbicia do odczytu danych z portu szeregowego -- wtedy dobrze nie używać przełącznika. Ale jako programista baz danych, który chce nadal używać C++, jestem stale fazowany z okropnym, trudnym do utrzymania kodem, który mapuje dane między członkami danych i konstrukcjami baz danych.

Ani zwiększenie serializacji, ani inny mechanizm tak naprawdę nie rozwiązują odbicia-musi to być zrobione przez kompilator -- a po jego wykonaniu C++ będzie ponownie rozwijany w szkołach i używany w oprogramowaniu zajmującym się przetwarzaniem danych

Dla mnie ten numer # 1 (A naitive threading primitives to numer # 2).

 1
Author: vsp,
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-09-02 21:04:48

Jest to w zasadzie dlatego, że jest to "opcjonalny dodatek". Wiele osób wybiera C++ zamiast języków takich jak Java i C# , aby mieć większą kontrolę nad wyjściami kompilatora, np. mniejszym i / lub szybszym programem.

Jeśli zdecydujesz się dodać odbicie, dostępne są różne rozwiązania.

 0
Author: Nick,
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 10:31:02