Ist C++ noch zu retten?



  • @john-0 sagte in Ist C++ noch zu retten?:

    Weil man Datentypen mit kleineren Wertebereich verlustfrei in größere Typen konvertieren kann

    unnötiger overhead. Außerdem entsteht dann die Notwendigkeit zu wissen mit welchem Wert die Variable initialisiert wurde.
    Was für kern-bescheuerter Vorschlag wenn man argumentiert dass auto code obfuscated.
    EDIT: Du hast mein Beispiel schon wieder ignoriert.

    for (auto i = 0; i != 1000; ++i) // <- auto deduziert als unsigned char?
    {
    }
    

    Argumentier mal warum das gut ist bitte.
    Hier bitte auch:

    auto foo()
    {
        return 2;
    }
    
    int main()
    {
        auto bla = foo(); // fml
    }
    

    Außerdem warum ziehst du dich an auto für Zahlen hoch?
    Auto für integrale datentypen interessiert doch kein mensch. Auto lohnt sich erst für komplexere Typen und rückgabewerte.

    @john-0 sagte in Ist C++ noch zu retten?:

    Literale existieren nur zum Übersetzungszeitpunkt und haben ihre eigenen Typen, die dann in die eigentliche C++ Typen konvertiert werden. In Deinem Beispiel entspricht das ull dem unsigned long long. Es ist aber kein Typealias oder Typedef für die POD-Typen, sondern ein eigener nur fürs Literal. Wozu werden parallel in einer Sprache drei komplette Sätze an Typen für PODs gepflegt: einer nur für Literale, einer für Formatstrings und dann einer für die eigentlichen Datentypen?

    Und jetzt erklär mir nochmal warum C++ mehr als ein Typsystem beinhaltet.

    @john-0 sagte in Ist C++ noch zu retten?:

    Ich sehe das halt nicht so. Für mich ist auto ein klarer Designfehler motiviert durch Faulheit, und Faulheit war noch nie ein guter Ratgeber für Designentscheidungen.

    Diese Sätze sind inhaltlich wertlos.

    EDIT: Ich hab nicht mehr die innerer Ruhe wie früher. Ich glaube ich stell das antworten hier erstmal ein, bevor ich verbal entgleise und ein bann kassier.



  • Ich sehe ebenfalls in auto einen großen Gewinn. Irgendwelche verschachtelten Typen, von denen man die Namen nicht mehr kennen muss, Benutzbarkeit von Iteratoren, Lambda-Funktionen (da ist der Typ nicht bekannt und auto ist erzwungen) etc.

    Wo ich auto nicht benutze:
    auto i {0};
    stattdessen finde ich dort
    int i = 0;
    einfach besser lesbar.

    Es kommt also - wie eigentlich immer - stark darauf an, worauf du in einem Codeteil gerade den Fokus legen willst. Wenn du rechnest und numerisch simulierst, finde ich konkrete Typen sehr hilfreich. Wenn es irgendein Proxy-Objekt ist, das irgendwo aus einer Klassenhierachie kommt und von dem du weißt, dass es die Member-Funktion int getMyData() hat, dann auf jeden Fall auto nehmen, denn was interessiert mich das konkrete Implementationsdetail "Typname", der da zwischendurch auftritt. Am Ende finde ich das int als Rückgabetyp, der wirklich von Interesse ist, wieder gut. Ähnlich mit den Iteratoren. Ich muss den Typen von *it kennen, der von it selbst ist mir wumpe. Ohne auto kommt bei Iteration über verschachtelte getemplatete Typen die helle Freude auf!

    Allerdings finde ich die Regeln für auto logisch und den Unterschied zwischen auto und auto & oder bevorzugt const auto& absolut eindeutig. Ich mache auch sehr viel Python, wo es duck typing gibt. Auch oft kein Problem. Man braucht die exakten Typen nicht überall zu kennen.

    Zu den Smartpointern: ich finde die gut, benutze aber zu weit überwiegend unique_ptr und nur selten shared_ptr (und das meist nicht wegen sharing, sondern weil ich einen custom deleter haben will und das durch das Type-Erasure einfach gemacht wird). Kommt sicher darauf an, was du so machst - ich habe das enable_share_from_this bislang nocht nicht gebraucht und kann mich daher hier nicht qualifiziert äußern. Nur: bist du sicher, dass dein Design sinnvoll ist? Gerade zirkuläre Abhängigkeiten und unklare Ownership können auf ein Designproblem hindeuten. (nicht falsch verstehen: können != müssen)



  • @Enumerator sagte in Ist C++ noch zu retten?:

    Was wenn ich hier das const weglasse.

    Na dann ist die Variable halt nicht const. Ansonsten ändert sich am Typ nichts.

    Das hast du ja mit gutem Grund hingeschrieben ;).

    Ich schreib da auch const hin wenn es int const ist. Weil es den Code einfacher zu lesen macht, wenn man bei der Definition schon sieht dass der initiale Wert nicht mehr geändert wird.

    Ist das Ganze dann immer noch lesbar wenn ich bei z. B. späteren Typkonvertierungen erstmal überlegen muss welchen Typ eine Variable überhaupt hat? Ich würde meinen Kollegen niemals ein solches Ratespiel zumuten.

    Ja ganz schlimm. [/sarcasm]
    Ich kenne komischerweise einige Leute die bei folgendem Code heulen und jammern was für ein furchtbares Ratespiel es doch ist den Typ zu ermitteln

    void fun(TypeA a, TypeB b) {
        auto const x = a + b;
        auto const y = fun2(x);
        auto const z = bar.foo();
        fun3(z);
    }
    

    aber folgenden Code 100.00% supi A-OK finden:

    void fun(TypeA a, TypeB b) {
        fun3(fun2(a + b).foo());
    }
    

    Und das ist einfach nur bescheuert.

    Die letzten Jahre habe ich zugegeben mit einigen Studienabsolventen zu tun gehabt und einfach gesehen, dass trotz dieser "Smarten" Klassen mehr Fehler gemacht wurden als von älteren Kollegen die sich noch die rohen Zeiger um die Ohren geschmissen hatten, um es etwas überspitzt zu formulieren.

    Leute mit weniger Erfahrung machen mehr Fehler als Leute mit mehr Erfahrung. Das ist Fakt. Und Studienabsolventen haben meist kaum Erfahrung was Programmieren angeht. Ebenso Fakt. Wenn du jetzt statt dessen behauptest dass die höhere Fehlerrate Schuld der Sprache ist, dann weiss ich nicht was ich noch dazu sagen soll.

    Zuweilen gingen die Fehler dann aber eben auf wenn man es streng sieht Unzulänglichkeiten der Sprache zurück.

    Ich hab in dem Absatz den du so schlimm fandest schon darauf hingewiesen dass C++ ein Tool für Profis ist. Das war kein Scherz. Ja, C++ ist komplex. Dafür kann man auch einiges damit machen. Dass jemand mit wenig C++ Erfahrung schlechten C++ Code mit vielen Fehlern schreiben wird, ist klar. Das heisst aber nicht dass C++ grundsätzlich eine Fehlkonstruktion ist -- und auch nicht dass die von dir kritisierten Features eine Fehlkonstruktion sind.

    U.a. nämlich auf die von mir genannten Probleme. Und ich würde einem C++ Anfänger daraus auch keinen Vorwurf machen. Den Vorwurf mache ich in diesen Fällen einfach C++.

    Den Vorwurf mache ich hier Leuten die Noobs einstellen und mit denen ohne die nötigen Prozesse Software für Flugzeugsteuerungen entwickeln wollen. Egal in welcher Sprache.

    Und andere Sprachen zeigen doch, dass es prinzipiell auch anders geht.

    Welche? Beispiel?

    Warum verteidigt ihr das dann noch?

    Ganz einfach: WEIL DU NICHT RECHT HAST. Wie dick kann ein Schädel sein? Ja, es gibt Sprachen die "sicherer" sind als C++. Die meisten davon sind aber auch deutlich weniger mächtig. Und was das wichtigste ist: Selbst mit der sichersten Sprache wirst du einen Affen oder auch durschnittlichen Studienabgänger nicht hinsetzen und Code schreiben lassen können, und dann erwarten dass der einfach so passt. (Also erwarten kannst du es schon, es wird bloss nicht funktionieren.) Jede Sprache die hinreichend mächtig ist, hat auch ordentlich Fehlerpotential. Das ignorierst du aber einfach. Muss Schuld der Sprache sein.

    Die Smart-Pointer - die shared_ptr Klasse insbesondere - fühlen sich einfach wie ein nachträglich hinzugefügtes Flickwerk an, weil man nie den Mut hatte die Sprachsyntax von C++ selber konsequent anzupassen. Das man das nicht getan hat kann ich auch nachvollziehen. Dafür gab es ja Grüde wie Abwärtskompatibilität, "You Pay Only For What You Use". Das Ergebnis ist halt nicht optimal.

    Ich hab dich vorhin schon gebeten mal konkrete Beispiele zu bringen was man genau besser machen könnte, und wie. So lange du das nicht tust, ist das Thema für mich durch.

    Ich programmiere nebenher ein Framework mit mittlerweile über 800 Klassen. Seit Jahren kämpfe ich praktisch nur noch mit sprachsyntaktischen Problemen die in anderen Sprachen einfach eleganter und solider gelöst sind bzw. werden können.

    Dann ist meine starke Vermutung, dass du einfach nicht gut C++ kannst. Wenn es dir schon zu kompliziert war dir zu merken dass man hier im Forum zitieren kann indem man > an den Anfang einer Zeile schreibt

    tadaa

    dann muss ich annehmen dass du von C++ quasi keinen Plan hast und das alles halb im Blindflug machst. Eine Annahme die auch gut zu den Beiträgen passt die du hier schreibst.

    Warum hat Qt wohl einen eigenen Präprozessor entwickelt? Doch nicht weil sie Lust dazu hatten, sondern weil die Möglichkeiten in C++ unzureichend waren.

    Qt ist halt auch sehr alt, da war von C++11 noch keine Rede.

    Mit den Problemen bin ich ja nicht alleine auf der Welt. C++ ist was moderne Entwicklung anbelangt einfach ein Stück weit abgehängt.

    "Moderne Entwicklung" ist aber auch alles andere als sauber oder sicher. Ich weiss also nicht was das genau für ein Massstab sein soll.

    Die std und boost sind aber auch teilweise von "Template Fetischisten" geschrieben. So krass habe ich das noch nie bei anderen Sprachen erlebt (Templategeschwurbel über 10 Zeilen usw.).

    Bitte gib mal zwei Beispiele von Sprachen die ebenso Templates haben und wo das weniger krass ist. Tip: C# zählt nicht denn C# hat keine Templates.



  • @5cript sagte in Ist C++ noch zu retten?:

    unnötiger overhead. Außerdem entsteht dann die Notwendigkeit zu wissen mit welchem Wert die Variable initialisiert wurde.

    Ah, also das was jetzt schon der Fall ist.

    Was für kern-bescheuerter Vorschlag wenn man argumentiert dass auto code obfuscated.
    EDIT: Du hast mein Beispiel schon wieder ignoriert.

    for (auto i = 0; i != 1000; ++i) // <- auto deduziert als unsigned char?
    

    Wo bitte ist denn der qualitative Unterschied zum status quo?

    for (auto i = 0; i != 10'000'000'000; ++i) {
    

    Das geht aktuell doch auch schon nicht. Der Punkt ist, dass an dieser Stelle auto Unfug ist.
    Schreibt man stattdessen

    for (auto i = 0, e = 10'000'000'000; i != e; ++i) {
    

    motzt der Compiler herum, dass die Deduktion des Typen nicht funktioniert, weil i und e unterschiedlich groß sein sollen. D.h. auch so löst man das Problem nicht. Man muss ohnehin explizit den Typ hinschreiben. Die beste Lösung ist es,

    for (size_t i = 0, e = 10'000'000'000; i != e; ++i) {
    

    zu schreiben bzw. den geeigneten Typ der Wahl. Das löst das Problem.

    Hier bitte auch:

    auto foo()
    {
        return 2;
    }
    
    int main()
    {
        auto bla = foo(); // fml
    }
    

    Warum sollte ich argumentieren, dass daran etwas gut sei? Ich bin derjenige, der sagt, dass auto schlecht ist. Aber wenn man sich damit auseinandersetzt, wo bitte ist denn der qualitative Unterschied zum status quo? Ob der Returntype von foo nun byte wäre oder int ist, macht doch qualitativ keinerlei Unterschied.

    Außerdem warum ziehst du dich an auto für Zahlen hoch?

    Weil das zu Problemen führt, wie Du ja gerade selbst zu erkennen beginnst.

    Auto für integrale datentypen interessiert doch kein mensch. Auto lohnt sich erst für komplexere Typen und rückgabewerte.

    Da spart es Code, aber zu einem Preis, dass verdeckt wird um welchen Typen es sich handelt. Es ist ja nicht so, dass man das Problem vor C++11 nicht gelöst hätte. auto erweitert die Sprache nicht um neue Konzepte, sondern erspart im besten Fall Schreibarbeit. Das Problem dabei ist, dass man i.d.R. Anforderungen an den gelieferten Typen stellt, d.h. man erwartet da als Rückgabewerte nicht irgend einen Typ sondern einen ganz konkreten. Man fängt hier also an mit impliziten Interfaces zu arbeiten und das widerspricht gerade dem Gedanken einer stark typisierten Programmiersprache. Wer SmallTalk will, soll's auch benutzen.

    Und jetzt erklär mir nochmal warum C++ mehr als ein Typsystem beinhaltet.

    Die Typbezeichner für die Literale und die PODs sind disjunkt. Man kann nicht ull a = 1'000'000'000 schreiben, sondern man muss unsigned long long oder auto auf der linken Seite verwenden, und umgekehrt kann man nicht auto a = unsigned long long 1'000'000'000 schreiben. D.h. Literale und PODs verwenden unterschiedlichen Typen, wobei man die Werte von Literale an korrespondierende POD Typen zuweisen kann.



  • @wob sagte in Ist C++ noch zu retten?:

    Allerdings finde ich die Regeln für auto logisch und den Unterschied zwischen auto und auto & oder bevorzugt const auto& absolut eindeutig. Ich mache auch sehr viel Python, wo es duck typing gibt. Auch oft kein Problem. Man braucht die exakten Typen nicht überall zu kennen.

    Genau darum geht es. C++ ist eine stark typisierte Sprache und eben keine schwachtypisierte Sprache wie etwa SmallTalk bei der man DuckTyping nutzt. Wenn man Interfaces nutzt, so soll das auch klar und deutlich dokumentiert sein und nicht implizit im Code verstreut sein. Jeder der länger C++ Templates nutzt kennt noch die absolut grausamen Fehlermeldungen, wenn man unpassende Typen an Templates übergab. Daraus erwuchs der Gedanke Konzepte in C++ einzuführen, weil implizite Interfaces zu total kryptisches Fehlermeldungen führte, da der Ort der Falschverwendung und der Ort der Fehlermeldung auseinander lagen bzw. sogar noch liegt.



  • @john-0 sagte in Ist C++ noch zu retten?:

    Genau darum geht es. C++ ist eine stark typisierte Sprache und eben keine schwachtypisierte Sprache

    Mir geht es aber darum, dass ich nicht an allen Stellen meines Codes die starken Typen brauche. Manchmal sind sie sinnvoll, dann nutzt man dort halt kein auto oder nur sporadisch auto. Manchmal aber machen starke Typen einem das Leben schwerer, ohne Mehrwert zu bieten - mehr noch, wenn man später man einen Typen in der Implementierung austauscht, muss man gleich überall Typen anpassen. Und wenn du jetzt argumentierst, dass Details nicht nach draußen leaken sollen, dann hast du generell zwar recht, aber oft lässt sich das nicht vermeiden oder es bietet Optimierungspotential oder die Implementierung wäre ungleich teurer (in Programmiererstunden).

    Für ein vollständiges Programm können in unterschiedlichen Stellen unterschiedliche Paradigmen sinnvoll sein.



  • Kleiner Hinweis: auto hat mit der Stärke des Typsystems nichts zu tun. Es ist ja sogar so, dass stark typisierte Sprachen oft mit sehr wenig expliziten Typangaben auskommen und der Rest inferiert wird.



  • @john-0 sagte in Ist C++ noch zu retten?:

    Wo bitte ist denn der qualitative Unterschied zum status quo?
    for (auto i = 0; i != 10'000'000'000; ++i) {

    Ja schreib ich jeden Tag sowas 🤣
    Nimm mal weniger drogen dude.



  • @5cript sagte in Ist C++ noch zu retten?:

    @john-0 sagte in Ist C++ noch zu retten?:

    Wo bitte ist denn der qualitative Unterschied zum status quo?
    for (auto i = 0; i != 10'000'000'000; ++i) {

    Ja schreib ich jeden Tag sowas 🤣

    Ich fand diesen Einwand von @john-0 durchaus berechtigt. Was ich schon sehr oft in Code gesehen habe, ist natürlich nicht die Loop von 1..10'000'000'000, sondern stattdessen:

    for (int i = 0; i != some_container.size(); ++i) { ... }
    
    // Oder dem folgenden, um die signed-Warnung zu ignorieren
    
    for (unsigned int i = 0; i != some_container.size(); ++i) { ... }
    

    Hältst du das für so abwegig?

    Nimm mal weniger drogen dude.

    Abstraktionsvermögen weg? Selber auf Droge? (sorry, wenn man anderen das unterstellt, unterstelle ich einfach mal zurück, auch wenn ich nicht angesprochen war)



  • Nein ist er nicht und zwar deswegen:
    warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<char>::size_type' {aka 'long long unsigned int'} [-Wsign-compare]|
    und wenn man einen 2 gigabyte vector (char, das kleinste was geht) auf x86_32 hat oder einen vector größerer größe auf 64 bit dann ist man sich dessen bewusst. Das kommt nicht einfach so "ups, der ist aber groß"

    wer auf microcontrollern einen vector hat, dem gratulier ich.

    (oder andere datenstruktur)

    EDIT: Oder noch breiter: KEIN MENSCH iteriert ohne sich das bewusst zu sein über milliarden an elementen.



  • Mir ist klar, das der Thread eher theoretischer Natur ist, aber wie schafft man in x86 einen vector mit 10'000'000'000 Elementen? Ich bekomme bei der Erstellung einen bad_alloc Fehler. Unter x64 kompiliert klappt das mit der Anzahl. Und dann kann man auch mit auto iterieren.

    int main()
    {
    	std::vector<char> vec(10'000'000'000);
    	std::cout << vec.size();
    
    	for (auto i = 0; i != vec.size(); ++i) {}
    	std::cout << "\nfertig";
    }
    


  • Gar nicht.
    Du kriegst das nie im leben als kontinuierlichen Block, außer vllt auf einem speziellen System?
    Es kommt drauf an wie viel RAM du hast und wie fragmentiert der ist.
    Und wegen addressing ist es sogar unmöglich in 32 bit.



  • Aber das ist dann doch kein Problem von auto, sondern wie man kompiliert?

    Ich versuche gerade

    for (auto i = 0; i != 10'000'000'000; ++i) {}
    

    aber da rechnet er schon seit über 10 Minuten.



  • Das muss ja kein Container sein, das könnte z.B. sowas wie ein Generator sein. Darum ging es jetzt glaub ich nicht primär.
    Aber so oder so, ich halte diese Diskussion für nicht relevant. auto für Zahlen finde ich auch völlig überflüssig, ändert aber nichts daran, dass auto ein sehr großer Gewinn für die Sprache war.



  • @zeropage sagte in Ist C++ noch zu retten?:

    aber da rechnet er schon seit über 10 Minuten.

    Ja genau. Mein reden.



  • Dieses auto- Beispiel war das einzige, was ich ohne größere Überlegungen nachtippen konnte. Und wenn das aber geht, dann verstehe ich eine auto-Problematik erst recht nicht.
    Wenn es mit autonicht geht, würde es mit jedem anderen Standard-Typen auch nicht gehen. Das hat doch dann nichts mit autozu tun?



  • Er hat gemeint es wäre besser wenn auto statt int bei auto i = 0 lieber den kleinst möglichen typ vom Wert ermitteln sollte, also hier unsigned char (oder char?, char wär falsch).

    wohingegen
    for (auto i = 0; i != 365; ++i) noch ein realistisches beispiel ist was man, oder ein anfänger schnell mal reinzimmert und sich dann wundert warum das ne endlosschleife ist, dachte er er könnte countern und sagen:
    "wieso? das hier geht doch jetzt auch schon nicht?"
    for (auto i = 0; i != SOMETHING_LUDICROUS; ++i)
    Dadurch enstand das erst.



  • Ok, danke 😉



  • @zeropage sagte in Ist C++ noch zu retten?:

    Unter x64 kompiliert klappt das mit der Anzahl. Und dann kann man auch mit auto iterieren.

    Kann man nicht, jedenfalls nicht so wie du. Denn deine Schleifenvariable ist ein int, kann also die Größe des Vektors gar nicht ausdrücken, jedenfalls nicht auf x64. Du bekommst irgendwann einen Überlauf. Das ist sogar undefiniertes Verhalten. Im günstigsten Fall ist es ein Überlauf, dein i kippt also bei 2,x Milliarden nach -2,x Milliarden über und wird niemals gleich 10 Milliarden sein, also hast du eine Endlosschleife.

    Du hast damit das Problem mit auto vorzüglich illustriert. Sogar ein Upvote dafür bekommen!



  • Oho, danke. Wenn auch unverdient. Mit size_t geht es dann aber. Wenn ich dort stattdessen auto einsetze, kommt keine entsprechende Ausgabe, wird dann nicht soweit gekommen sein. Habe ich mich von der "fertig"-Meldung blenden lassen.

    int main()
    {
    	std::vector<char> vec(10'000'000'000);
    	std::cout << vec.size();
    
    	std::size_t n = 0;
    	for (std::size_t i = 0; i != vec.size(); ++i)
    	{
    		++n;
    		if (n == 3'000'000'000)
    			std::cout << "\n3 Milliarden: " << n;
    	}
    	std::cout << "\nfertig";
    }
    

Anmelden zum Antworten