Kleine Umfarge: C++09 oder Ur-C++



  • ich finds sehr schade dass wir kein restrict gekriegt haben. Ich seh nicht ein wozus einen null_ptr braucht. Ich haette mir Concepts gewuenscht, schade dass man da keine Loesung gefunden hat.
    Ich finds gut, dass auf Multitthreading eingegangen wird. Ich freu mich auf lambdas.

    Alles in Allem: der Standard haette besser sein koennen, aber er ist sicher eine Verbesserung zu C++98. Sobald es einen brauchbaren Compiler gibt, werd ich aber vermutlich auf D 2.0 umsteigen.



  • Blue-Tiger schrieb:

    Ich seh nicht ein wozus einen null_ptr braucht.

    http://channel9.msdn.com/shows/Going+Deep/Stephan-T-Lavavej-Everything-you-ever-wanted-to-know-about-nullptr/

    Allgemein gibt es auch noch einen 5 teiligen msdn webcast.
    Dieser stellt zwar nur die quasi Standard features von VS2010 vor, aber ist sicherlich auch für den einen oder anderen interessant:
    hier mal ein link für den 1. teil:
    http://www.microsoft.com/germany/msdn/webcasts/library.aspx?id=1032455633


  • Administrator

    Blue-Tiger schrieb:

    Ich seh nicht ein wozus einen null_ptr braucht.

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-220511.html

    player424 schrieb:

    Die Lambda-Funktionen z.B. finde ich unnötig. Ich hatte jetzt noch keinen wirklichen Fall, wo sie einen erkennbaren Mehrwert gebracht hätten. Dem Nutzen dieser Spracherweiterung steht dann auch die etwas undurchsichtige Syntax entgegen.

    Die Syntax ist wirklich alles andere als schön gewählt. Da stimme ich dir zu. Aber praktisch ist es definitiv. In C# verwende ich an sehr vielen Stellen Lambda-Ausdrücke. In Verbindung mit den Algorithmen aus der Standardbibliothek ist das sehr praktisch. So muss man keine zusätzliche Funktoren mehr schreiben und kann direkt Lambda-Ausdrücke verwenden.

    Nehmen wir ein ganz einfaches Beispiel:

    class User
    {
    private:
      std::string m_firstname;
      std::string m_lastname;
    
      // ...
    
    public:
      std::string const& get_firstname() const { return m_firstname; }
      std::string const& get_lastname() const { return m_lastname; }
    
      // ...
    };
    
    // Wir haben nun einen std::vector<User> mit Werten gefüllt:
    std::vector<User> users;
    
    // Nun möchten wir diesen nach dem Nachnahmen sortieren:
    auto lastname_cmp = [](User const& lhs, User const& rhs) { lhs.get_lastname() < rhs.get_lastname(); };
    std::sort(users.begin(), users.end(), lastname_cmp);
    
    // Oder doch lieber nach dem Vornamen?
    auto firstname_cmp = [](User const& lhs, User const& rhs) { lhs.get_firstname() < rhs.get_firstname(); };
    std::sort(users.begin(), users.end(), firstname_cmp);
    

    Sowas ist sehr praktisch. Man kann auch einfach Prädikate und anderes erstellen, was man in Zusammenhang mit solchen Algorithmen verwenden kann.

    player424 schrieb:

    Das auto Keyword ist auch eine ganz nette Sache, aber auch dabei habe ich die Bedenken, dass es den Quellcode unleserlicher machen könnte.

    Gerade das Gegenteil ist der Fall. Vielfach interessiert mich der Typ gar nicht gross, ich will nur den korrekten Verwenden. Wieder ein einfaches Beispiel:

    template<typename T>
    typename std::map<std::string, std::pair<std::string, T> >::reverse_const_iterator foo();
    
    // ...
    std::map<std::string, std::pair<std::string, int> >::reverse_const_iterator iter = foo<int>();
    
    // zu:
    auto iter = foo<int>();
    

    Was sieht übersichtlicher aus? 🤡
    In C# gibt es var und ich verwende es mehr und mehr, weil es die Übersicht extrem erhöht. Zum Beispiel auch für ganz einfache Dinge:

    var obj = new ThisIsAClass();
    // sieht doch besser aus als:
    ThisIsAClass obj = new ThisIsAClass();
    

    Gleiches könnte man sich auch für C++ vorstellen:

    auto obj = new ThisIsAClass();
    

    Es ist sowieso klar, dass obj vom Typ ThisIsAClass* sein wird, da müssen wir dies nicht noch explizit angeben.

    player424 schrieb:

    Aus der Standardbibliothek hätte man, meiner Meinung nach auch die Klasse für statische Arrays und Tuples auslassen können.

    Und was ist der Grund? Beides Dinge, welche ich regelmässig brauchen werde.

    Grüssli



  • Bei dem Compilersupport mache ich mir nicht so viele Sorgen. Die Compilerbauer sind ja schon fleißig dabei die Unterstützung einzubauen (http://gcc.gnu.org/projects/cxx0x.html) und laut Stroustrup hat man diesmal auch mehr auf die Compilerbauer gehört, um so etwas wie export zu vermeiden.

    player424 schrieb:

    Die Lambda-Funktionen z.B. finde ich unnötig. Ich hatte jetzt noch keinen wirklichen Fall, wo sie einen erkennbaren Mehrwert gebracht hätten. Dem Nutzen dieser Spracherweiterung steht dann auch die etwas undurchsichtige Syntax entgegen.

    Bei Lambda-Funktionen finde ich die Syntax nur hässlich. Ansonsten sind sie imho sehr nützlich, da man ja oft mal schnell einen Funktor braucht und derzeit dafür dann irgend wo eine kleine Klasse basteln muss.

    player424 schrieb:

    Das auto Keyword ist auch eine ganz nette Sache, aber auch dabei habe ich die Bedenken, dass es den Quellcode unleserlicher machen könnte.

    Es macht in den meisten Situationen den Code sicher leserlicher.

    zB was bei mir öfters im Code vorkommt:

    template<typename Lhs, typename Rhs>
    typename detail::add<Lhs, Rhs>::type
    add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    wird mit auto imho deutlich lesbarer

    template<typename Lhs, typename Rhs>
    auto add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    player424 schrieb:

    Aus der Standardbibliothek hätte man, meiner Meinung nach auch die Klasse für statische Arrays und Tuples auslassen können.

    Was?! Nein! Beides sind imho sehr sinnvolle Klassen, die ich auch regelmäßig benutze.



  • Teilt ihr euch ein Gehirn 🙂 ?

    OK bei den Sprachfeatures kann man wohl sagen: wenn mans mal braucht ist es bestimmt nützlich. Aber wozu braucht ihr eine Klasse für etwas, dass schon fest in der Sprache eingebaut ist (std::array)? Und was die Tuple-Klasse angeht: Mir fallen spontan nur wenige Beispiele ein, bei denen man so etwas brauchen könnte.



  • rüdiger schrieb:

    player424 schrieb:

    Die Lambda-Funktionen z.B. finde ich unnötig. Ich hatte jetzt noch keinen wirklichen Fall, wo sie einen erkennbaren Mehrwert gebracht hätten. Dem Nutzen dieser Spracherweiterung steht dann auch die etwas undurchsichtige Syntax entgegen.

    Bei Lambda-Funktionen finde ich die Syntax nur hässlich. Ansonsten sind sie imho sehr nützlich, da man ja oft mal schnell einen Funktor braucht und derzeit dafür dann irgend wo eine kleine Klasse basteln muss.

    Dass sie nützlich sind steht eigentlich außer Frage. Die Alternativen dazu sind:
    (1) Code des Funktors irgendwo anders ablegen --> zerstört den Lesefluss
    (2) Eventuell kriegt man ein entsprechendes Funktionsobjekt mit std::bind hin. Lesen+Verstehen ist bei dem, was dabei raus kommt, schwieriger.

    Ich denke die Syntax ist auch nur eine Frage der Gewöhnung. Es gefällt mir sogar, dass ich über die "Capture-Clause" steuern kann, was kopiert und was referenziert wird vom "Umfeld". Ich hätte mir allerdings noch ein "move-capture" gewünscht, zB so etwas:

    template<class Fun>
    void exec(Fun fun) {
      fun();
    }
    
    void foobar()
    {
      unique_ptr<int> p (new int);
      *p = 1729;
      exec([-p]{ // <-- kein C++0x, aber so könnte ein "move-capture" aussehen.
        cout << *p << '\n';
      });
    }
    

    auto...

    rüdiger schrieb:

    Es macht in den meisten Situationen den Code sicher leserlicher.
    zB was bei mir öfters im Code vorkommt:

    template<typename Lhs, typename Rhs>
    typename detail::add<Lhs, Rhs>::type
    add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    wird mit auto imho deutlich lesbarer

    template<typename Lhs, typename Rhs>
    auto add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    Das wär nett gewesen. Aber Du kannst nicht einfach den return-Typ weglassen. Oder meintest Du

    template<typename Lhs, typename Rhs>
    auto add(Lhs const &lhs, Rhs const &rhs) -> decltype(lhs+rhs) {
      ...
    }
    

    ?

    Die Alternative dazu wäre

    template<typename Lhs, typename Rhs>
    decltype(declval<Lhs const&>()+declval<Rhs const&>())
    add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    Innerhalb von decltype habe ich lhs durch declal<Lhs const&>() ersetzt. Wir brauchen keine extra detail::add-Klasse schreiben. declval ist dazu da, für decltype-Ausdrücke Objekte herzuzaubern. 🙂

    rüdiger schrieb:

    player424 schrieb:

    Aus der Standardbibliothek hätte man, meiner Meinung nach auch die Klasse für statische Arrays und Tuples auslassen können.

    Was?! Nein! Beides sind imho sehr sinnvolle Klassen, die ich auch regelmäßig benutze.

    Sehe ich auch so.

    kk



  • Die echten Probleme von C++ löst der neue Standard auch nicht, weil er dann nicht mehr kompatibel wäre.



  • rüdiger schrieb:

    wird mit auto imho deutlich lesbarer

    template<typename Lhs, typename Rhs>
    auto add(Lhs const &lhs, Rhs const &rhs) {
      ...
    }
    

    wäre übersichtlicher, geht so aber soweit ich weiss nicht 😉



  • player424 schrieb:

    Die Lambda-Funktionen z.B. finde ich unnötig.

    Du findest den ultimativen Imperativ unnötig?
    🙂


  • Administrator

    player424 schrieb:

    Aber wozu braucht ihr eine Klasse für etwas, dass schon fest in der Sprache eingebaut ist (std::array)?

    std::array und ein C-Array sind zwei grundverschiedene Sachen. Ein C-Array ist äusserst flüchtig und du endest im Nu mit einem Zeiger ohne Informationen über die Grösse des Arrays oder eine sinnvolle Möglichkeit das Array zu kopieren. std::array gibt dir wirklich ein Array mit konstanter Grösse, welches du auch als Array behandeln kannst. Grundsätzlich wird std::array das C Array vollständig ablösen können.

    player424 schrieb:

    Und was die Tuple-Klasse angeht: Mir fallen spontan nur wenige Beispiele ein, bei denen man so etwas brauchen könnte.

    Immer wenn man etwas kurz gruppieren möchte. Das kann für viele Fälle sinnvoll sein, von Rückgabewerte bis hin zu Algorithmen, wo man temporär Dinge zusammenbinden muss. Das praktische ist, man muss nicht immer wieder eine eigene kleine Struktur basteln. Ist grundsätzlich ähnlich wie die Lambda-Ausdrücke, es erleichtert einem das Leben.

    Grüssli



  • Gibts denn auch ne schöne möglichkeit darauf zu zu greifen? das std::get<n> konstruk ist irgendwie ziemlich... groß.



  • Der ganze STL Kram ist allgemein sehr klobig. Schau dir mal std::cout an. Damit etwas tabellarisch auszugeben ist genau so eleganz wie einen essay in den schnee zu pinkeln. Dagegen ist das C typische printf elegant und schlank.



  • Der ganze STL Kram ist allgemein sehr klobig.

    Ist es klobig std::vector<MyClass> zu schreiben und dann ein dynamisches Array von Objekten der Klasse MyClass zu haben?

    Schau dir mal std::cout an.

    std::cout gehört nicht zu Standard Template Library... 🙄

    Damit etwas tabellarisch auszugeben ist genau so eleganz wie einen essay in den schnee zu pinkeln. Dagegen ist das C typische printf elegant und schlank.

    ...und fehleranfällig.



  • Wenn man keine Ahnung hat schrieb:

    ...und fehleranfällig.

    Aber nur wenn man wild castet, das geht in C++ dann wohl auch schief. Wenn man das nicht tut, kann der Compiler die Typen genauso überprüfen wie überall sonst.
    🙂


  • Mod

    mngbd schrieb:

    Wenn man keine Ahnung hat schrieb:

    ...und fehleranfällig.

    Aber nur wenn man wild castet, das geht in C++ dann wohl auch schief. Wenn man das nicht tut, kann der Compiler die Typen genauso überprüfen wie überall sonst.
    🙂

    Dummerweise funktioniert das aber auch nur, weil es eine überschaubare Anzahl von zulässigen Datentypen für printf gibt. In einer Sprache wo eigene Datentypen ganz alltäglich sind, geht das Konzept nicht mehr auf.



  • mngbd schrieb:

    Wenn man keine Ahnung hat schrieb:

    ...und fehleranfällig.

    Aber nur wenn man wild castet, das geht in C++ dann wohl auch schief. Wenn man das nicht tut, kann der Compiler die Typen genauso überprüfen wie überall sonst.
    🙂

    Dann sag mir, wo du hier einen Cast siehst 🙂

    char x = 12;
    
    // ...
    
    printf("%d", x);
    

    Kompiliert, ist aber undefiniert.



  • Das ist nicht undefiniert. Befass dich mal mit integer promotions.



  • Dann versuchs mit

    char x = 12;
    printf("%s", x);
    


  • DerKuchen schrieb:

    Dann versuchs mit

    char x = 12;
    printf("%s", x);
    

    Zumindest der gcc erkennt den Unsinn:

    $ gcc -x c -std=c99 -Wall -
    #include <stdio.h>
    
    int main(void) {
      char x = 12;
      printf("%s", x);
    } 
    <stdin>: In Funktion »main«:
    <stdin>:5: Warnung: format »%s« erwartet Typ »char *«, aber Argument 2 hat Typ »int«
    

    🙂



  • Zumindest der gcc erkennt den Unsinn

    Er erkennt ihn vielleicht, aber er verhindert ihn nicht.
    Der aktuelle MS Compiler gibt übrigens (in der Standardeinstellung) keine Warnung aus.


Anmelden zum Antworten