Kleiner Code mit 6 Warnings die ich nicht verstehe!



  • guten moregen^^

    ::tolower
    liegt eigtl nicht im globalen scope (nur die C-fkt tut das)
    und das include fehlt auch
    </kruemelkack>

    bb 🤡



  • Dann versuch den Code mal mit tolower statt ::tolower zu kompilieren 🤡



  • #include <iostream>
    #include <algorithm> // fr transform
    #include <string>
    #include <cctype> // für tolower
    
    using namespace std;
    
    int main()
    {
        string name;
    
        cout << "Wie ist Ihr Name? " << endl;
        cin >> name;
    	transform(name.begin(), name.end(), name.begin(), tolower);
        if(name == "marcel")
        {
            cout << "Marcel ist ein Name, den sehr viele Schwule tragen.";
        }
        else if(name == "alex")
        {
            cout << "Alex ist ein Name eines starken Kriegers und Sexgottes.";
        }
        else if(name == "oliver")
        {
            cout << "Oliver ist ein Name, den viele Dorftrottel tragen.";
        }
        else
        {
            cout << "Der Name sagt mir nichts.";
        }
    }
    

    So sollte es Standardkonform sein.


  • Administrator

    Qwert Zuiopü schrieb:

    So sollte es Standardkonform sein.

    Um genau zu sein eigentlich nicht ganz. Das C und C++ Locale unterschieden sich.

    Ich würde es daher eher so machen:

    #include <locale> // für tolower
    #include <string>
    #include <iostream>
    #include <algorithm> // für transform
    
    // Hilfsfunktion:
    char doLower(char c)
    {
        return std::tolower(c, std::locale());
    }
    
    int main()
    {
        std::string name;
    
        std::cout << "Wie ist Ihr Name? " << std::endl;
        std::getline(std::cin, name);
    
        std::transform(name.begin(), name.end(), name.begin(), &doLower);
    
        if(name == "marcel")
        {
            std::cout << "Marcel ist ein Name, den sehr viele Schwule tragen.";
        }
        else if(name == "alex")
        {
            std::cout << "Alex ist ein Name eines starken Kriegers und Sexgottes.";
        }
        else if(name == "oliver")
        {
            std::cout << "Oliver ist ein Name, den viele Dorftrottel tragen.";
        }
        else
        {
            std::cout << "Der Name sagt mir nichts.";
        }
    
        return 0;
    }
    

    Grüssli 😉



  • Hey lieben Dank für eure Antworten und Vorschläge. Bringt mich ans Ziel :):):)... Erstaunlich das wirklich jeder geholfen hat, und nicht ein klugscheißer Spruch kam 🙂 ich sollte mich hier registrieren *g*

    Lg
    LukeJones



  • Eine Anmerkung:

    char doLower(char c)
    {
        return std::tolower(c, std::locale());
    }
    

    ist keine besonders performante Lösung; es wird für jedes Zeichen ein neues std::locale-Objekt erstellt. Das mag für ein einzelnes Objekt nicht lange dauern, aber es läppert sich auf die Dauer halt.

    Besser, wenn auch etwas komplexer, ist

    // Zusätzlic oben:
    #include <functional>
    
    // ...
    
    std::transform(name.begin(),
                   name.end  (),
                   name.begin(),
                   std::bind2nd(std::ptr_fun(std::tolower<char>), std::locale()));
    

    oder, wenn TR1 verfügbar ist,

    using std::tr1::bind;
    using std::tr1::placeholders::_1;
    
    std::transform(name.begin(),
                   name.end  (),
                   name.begin(),
                   bind(std::tolower<char>, _1, std::locale()));
    

    auf die Art hält das Funktionsobjekt das locale-Objekt und kann es für jeden Buchstaben erneut verwenden.


  • Administrator

    seldon schrieb:

    ist keine besonders performante Lösung;

    1. Es ist ein Beispiel.
    2. Hast du es gemessen?
    std::locale von der Dikumware Library ist zum Beispiel über PIMPL implementiert. Der Defaultkonstruktor holt sich das globale C++ Locale. Grundsätzlich wird nicht viel mehr gemacht, als der Zeiger auf dieses globale Locale zu holen und einen Referenzzähler zu erhöhen. Wahnsinnig aufwendig ...

    Wie ging das schon wieder mit diesem Premature ... Optimization ... irgendwas Zeug? 🙂

    seldon schrieb:

    Besser, wenn auch etwas komplexer, ist

    // Zusätzlic oben:
    #include <functional>
    
    // ...
    
    std::transform(name.begin(),
                   name.end  (),
                   name.begin(),
                   std::bind2nd(std::ptr_fun(std::tolower<char>), std::locale()));
    

    Das hast du eindeutig nicht getestet. Es kompiliert nämlich nicht. Das ist der typische Unsinn von bind2nd und Konsorten. tolower erwartet nämlich als zweiten Paramter ein std::locale const& . Eine Referenz auf ein konstantes Objekt und sowas wird nicht unterstützt.

    Grüssli



  • gcc 4.4.3 frisst es und spuckt nicht mal mit -Wall -Wextra -pedantic ne Warnung aus. Warum sollte er auch, konstante Referenzen können an temporäre Objekte gebunden werden.

    Und natürlich ist die Konstruktion einer locale nicht besonders komplex, aber er macht das für jedes einzelne Zeichen wieder. Ich hab die Dinkumware-Bibliothek jetzt nicht vor Augen, aber wenn der mit einem Referenzzähler arbeitet, muss er nicht nur einen Zeiger setzen und den Referenzzähler erhöhen, sondern außerdem für jedes Zeichen auf dem Rückweg den Referenzzähler wieder senken, kucken, ob er Null ist, und ggf. das eigentliche Objekt zerstören. In der libstdc++ muss er zusätzlich noch einen Mutex bedienen, und es sollte mich nicht wundern, wenn Dinkumware das genau so macht.

    Wenn du jetzt regelmäßig mit Massen von Text arbeitest, macht sich das irgendwann bemerkbar.


  • Administrator

    seldon schrieb:

    gcc 4.4.3 frisst es und spuckt nicht mal mit -Wall -Wextra -pedantic ne Warnung aus. Warum sollte er auch, konstante Referenzen können an temporäre Objekte gebunden werden.

    Wir verstehen uns aber schon richtig, es geht hier um bind2nd . Das bind aus dem tr1 funktioniert schon 😉
    Das bind2nd führt am Ende zu einer Referenz auf eine Referenz. Das ist natürlich ein Fehler.

    Kurz verdeutlich, um was für Typen es sich handelt:

    std::ptr_func gibt zurück:
    std::pointer_to_binary_function<char, std::locale const&, char>
    
    Der second_argument_type von dieser Klasse ist somit std::locale const&.
    Der Konstruktor von std::binder2nd sieht wie folgt aus:
    binder2nd(Operation const& op,  typename Operation::second_argument_type const& x)
    
    Somit würde hier eine Referenz zu einer Referenz erwartet und das ist illegal.
    

    Auf dem MSVC hat dies jedenfalls noch nie funktioniert.

    seldon schrieb:

    Wenn du jetzt regelmäßig mit Massen von Text arbeitest, macht sich das irgendwann bemerkbar.

    Jetzt setzt du plötzlich Randbedingungen hin, die waren aber vorhin nicht vorhanden. Und auch in dem Fall würde mich wirklich noch wunder nehmen, wieviel das nun tatsächlich ausmacht. Einfach pauschal annehmen ist keine so gute Idee 😉

    Grüssli



  • wenn ich das alles mal verstehen würde *g 🙂 gibts ein wörterbuch?


  • Administrator



  • nun das war direkt! Gute Antwort *hrhr*.. Also wenn ich meinen C++ Tagesablauf beschreiben würde, würde ich sagen:

    70% Lesen, 20% Fehlersuchen und 10% Sprechen/Programmieren... Hoffe das ändert sich im laufe der Zeit



  • Ja, der bind2nd-Kram kompiliert problemlos. Ich hab jetzt keinen MSVC da, um zu testen, ob der das frisst.

    Und von wegen "plötzlich Randbedingungen," das ist Blödsinn. Performance macht bei Kleinkram nie einen Unterschied; natürlich ist das erst von Interesse, wenn man große Datenmengen hin- und herwälzt. Oder wenig Rechenzeit zur Verfügung hat.

    Der Laufzeitunterschied ist bei mir ein Faktor von etwa 0,7-0,75 mit -O2. Benutzt habe ich folgendes Benchmarkprogramm:

    #include <algorithm> 
    #include <ctime>
    #include <functional>
    #include <iostream>
    #include <locale>
    #include <string>
    
    char do_lower(char c) {
      return std::tolower(c, std::locale());
    }
    
    int main()
    {
      std::string s (100000000, 'A');
      std::string s2(100000000, 'A');
    
      std::clock_t t1 = clock();
    
      std::transform(s.begin(),
                     s.end  (),
                     s.begin(),
                     std::bind2nd(std::ptr_fun(std::tolower<char>), std::locale()));
    
      std::clock_t t2 = clock();
    
      std::transform(s2.begin(),
                     s2.end  (),
                     s2.begin(),
                     &do_lower);
    
      std::clock_t t3 = clock();
    
      std::cout << t2 - t1 << ", " << t3 - t2 << std::endl;
    }
    

    Ich nehme aber an, dass das Ergebnis von Compiler zu Compiler variieren wird.

    Übrigens, um jetzt mal wirklich neue Randbedingungen einzuwerfen, es ist wegen des Mutex im Konstruktor davon auszugehen, dass sich der Abstand bei Multithreading noch stark vergrößert.


  • Administrator

    seldon schrieb:

    Ja, der bind2nd-Kram kompiliert problemlos. Ich hab jetzt keinen MSVC da, um zu testen, ob der das frisst.

    g++ kann es übrigens auch nicht, wenn man die g++ Erweiterungen korrekt abstellt:

    g++ -o cpptest.exe -std=c++98 -pedantic cpptest.cpp
    

    Gerade getestet.

    seldon schrieb:

    Und von wegen "plötzlich Randbedingungen," das ist Blödsinn. Performance macht bei Kleinkram nie einen Unterschied; natürlich ist das erst von Interesse, wenn man große Datenmengen hin- und herwälzt. Oder wenig Rechenzeit zur Verfügung hat.

    Das hast du oben einfach implizit angenommen. Du redest aber hier mit Anfängern, da ist diese implizite Annahme ein Fehler. Es klingt nämlich fast so, als würde es im Beispiel zu einem Problem deswegen kommen.

    Und ich frage mich immer noch, wo es am Ende wirklich einen Unterschied machen wird. Natürlich ist es langsamer, dass musst du mir nicht beweisen. Die Frage ist aber, in welchem Kontext wird es schlussendlich wirklich ins Gewicht fallen 😉

    Grüssli


Anmelden zum Antworten