UnaryFunction in c++ 17



  • @Arcoth

    Die steht dort, wo sie immer stand.

    Nein tut sie nicht! camper hat einen Eintrag erstellt der nun nicht mehr da ist.
    Egal tut nichts zur sache.

    Natürlich. Ich dachte, es ginge darum, Duplikate zu entfernen, nicht, sie gar nicht erst als Input zuzulassen?
    

    Echt wo habe ich das geschrieben? Eigentlich war die Anforderung nur Elemente hinzufügen wenn keine doppelten vorhanden sind.

    Aber ist hier auch nicht so wichtig. Lassen wir das auch erst mal ausser acht.

    Beispiel:
    Ich muss doch sagen dass ein Element vom typ blabla sein muss sonst erhalte ich keinen Kompilierungsfehler sondern einen Laufzeitfehler.


  • Mod

    Die find-Memberfunktion set von set hat einen Templateoverload, es ist also nicht zwingend erforderlich ein Containerelement zu erstellen, sofern der set-Kompartor das erlaubt. Das Beispiel hier macht es vor.


  • Mod

    booster schrieb:

    Die steht dort, wo sie immer stand.

    Nein tut sie nicht! camper hat einen Eintrag erstellt der nun nicht mehr da ist.
    Egal tut nichts zur sache.

    😮 camper, hast du etwa deine Moderatorenrechte dazu missbraucht, einen unsinnigen Beitrag zu vertuschen? So etwas machen wir doch nie! 🤡

    Echt wo habe ich das geschrieben?

    Exceptions? Also, das C++-Feature war gemeint, nahm ich an?


  • Mod

    Arcoth schrieb:

    booster schrieb:

    Die steht dort, wo sie immer stand.

    Nein tut sie nicht! camper hat einen Eintrag erstellt der nun nicht mehr da ist.
    Egal tut nichts zur sache.

    😮 camper, hast du etwa deine Moderatorenrechte dazu missbraucht, einen unsinnigen Beitrag zu vertuschen? So etwas machen wir doch nie! 🤡

    Ich gebrauche Löschrechte, falls ich schnell genug merke, Unsinn geschrieben zu haben, und noch keiner geantwortet hat. Und das ist schließlich immer noch da. K.A. was vermisst wird.

    Der Beitrag in diesem Thread, den ich gelöscht habe, war nur ein *facepalm* zu deiner Behauptung, tag-Dispatching wäre nicht erforderlich, nur im gleichzeitig im Beispiel die Codegenerierung anhand eines tags (per if constexpr) zu beeinflussen. Irgendwas Neues war da nicht drin.



  • @camper
    Ja ok das ist aber erst seit c++ 14 der Fall?
    Den Code den ich hier habe wurde vor 10 Jahren erstellt. Also wie gesagt ich versuche das alles umzusetzen und den Code zu optimieren. Aber zuerst muss ich das Projekt mal wieder zum laufen zu bringen.

    Also ich versuche es mal anders zu beschreiben:

    Möchte ich über die einzelnen Elemente iterieren die ich erhalte und dann auf ein Element zugreifen muss ich doch klar stellen dass ein Element vom typ MyVariable sein muss:

    class someclass
    {
        template <typename Iter>
        void do_something(Iter first, Iter last)
        {
            Iter temp(first);
            for(; temp != last; ++temp)
            {
                auto x = (*temp)->GetName();
                //...
            }
        }
    }
    

    Muss ich jetzt nicht so was machen:

    template <typename Iter, typename = std::enable_if<std::is_same_v<shared_ptr<MyVariable>, typename std::iterator_traits<Iter>::value_type >>>
    void do_something(Iter first, Iter last)
    


  • K.A. was vermisst wird.

    den ich gelöscht habe, war nur ein *facepalm* zu deiner Behauptung, tag-Dispatching wäre nicht erforderlich

    Ja den habe ich gemeint. Du hattest den Beitrag von Arcoth mit tag dsipatching zitiert und den *facepalm* drunter gesetzt. Den hatte ich gesehen. Und sofort darauf geantwortet. Also dann meine Antwort online war, war wiederum dein Eintrag weg. 😕


  • Mod

    booster schrieb:

    Möchte ich über die einzelnen Elemente iterieren die ich erhalte und dann auf ein Element zugreifen muss ich doch klar stellen dass ein Element vom typ MyVariable sein muss:

    class someclass
    {
        template <typename Iter>
        void do_something(Iter first, Iter last)
        {
            Iter temp(first);
            for(; temp != last; ++temp)
            {
                auto x = (*temp)->GetName();
                //...
            }
        }
    }
    

    Muss ich jetzt nicht so was machen:

    template <typename Iter, typename = std::enable_if<std::is_same_v<shared_ptr<MyVariable>, typename std::iterator_traits<Iter>::value_type >>>
    void do_something(Iter first, Iter last)
    

    C++ ist eine statisch typisiserte Sprache. Der einzige Unterschied zwischen der Variante mit enable_if gegenüber ohne ist die Art des Fehlers, die der Compiler liefert.
    Mit enable_if wird sich der Compiler beschweren, keine passende Überladung für den Funktionsaufruf gefunden zu haben, wenn der Iteratortyp nicht passt.
    Ohne enable_if wird während der Instantiierung der Funktion irgendetwas schiefgehen.

    enable_if dient primär dazu, zwischen mehreren andernfalls kollidierenden Überladungen auswählen zu können.
    Wenn sowieso klar ist, welche Überladung gemeint sein soll, wird enable_if eher verwirren. Wenn überhaupt käme ein static_assert innerhalb des Templates in Frage.
    z.B.

    template <typename InputIter, typename Func>
    void Add(InputIter begin, InputIter end, Func f)
    {
        static_assert(std::is_same_v<decltype(f(*begin)), decltype(_varMap)::mapped_type>);
        decltype(_varMap) tmp;
        for ( ; begin != end; )
        {
            auto var = f(*begin++);
            if (_varMap.find(var->GetName()) != _varMap.end())
                return;
            tmp.try_emplace(var->GetName(), var);
        }
        _varMap.merge(tmp);
    }
    

  • Mod

    booster schrieb:

    K.A. was vermisst wird.

    den ich gelöscht habe, war nur ein *facepalm* zu deiner Behauptung, tag-Dispatching wäre nicht erforderlich

    Ja den habe ich gemeint. Du hattest den Beitrag von Arcoth mit tag dsipatching zitiert und den *facepalm* drunter gesetzt. Den hatte ich gesehen. Und sofort darauf geantwortet. Also dann meine Antwort online war, war wiederum dein Eintrag weg. 😕

    data race 🙂 Vermutlich hast du innerhalb der 2-5 Sekunden zwischen meinem letztem Refresh und der Auslösung des Löschvorgangs gepostet.



  • @camper.

    Das sieht gut aus 👍

    template <typename InputIter, typename Func>
    void Add(InputIter begin, InputIter end, Func f)
    {
        static_assert(std::is_same_v<decltype(f(*begin)), decltype(_varMap)::mapped_type>);
        decltype(_varMap) tmp;
        for ( ; begin != end; )
        {
            auto var = f(*begin++);
            if (_varMap.find(var->GetName()) != _varMap.end())
                return;
            tmp.try_emplace(var->GetName(), var);
        }
        _varMap.merge(tmp);
    }
    

    Auf den ersten Blick top 🙂

    Wenn ich nun eine überladung habe ohne "Func" und möchte die weiterleiten.
    Wie würde dass aussehen so in etwa?

    template<class Iter>
    void Add(Iter first, Iter last)
    {
         Add(first, last, [](Iter elem) { return *elem; });
    }
    

  • Mod

    template<class Iter>
    void Add(Iter first, Iter last)
    {
         Add(first, last, [](Iter elem) { return *elem; });
    }
    

    Der Funktor soll *it übergeben bekommen,
    also

    Add(first, last, [](const typename std::iterator_traits<Iter>::value_type& elem) { return elem; });
    

    was unnötig Schreibarbeit ist. Deshalb:

    Add(first, last, [](auto& elem) { return elem; });
    

    Der Compiler kann den Typ schliesslich auch selbst herausfinden.



  • Add(first, last, [](auto& elem) { return *elem; });
    

    Dann war das was ich gepostet hatte gar nicht so falsch 🙂

    jetzt erhalte ich aber noch einen Kompilierungsfehler.

    try_emplace<>(std::basic_string<char,std::char_traits<char>,std::allocator<char>> &&)': cannot convert argument 1 from 'std::shared_ptr<_Ty>' to 'const std::basic_string<char,std::char_traits<char>,std::allocator<char>> &'

    tmp.try_emplace(var->GetName(),var);
    

    muss es wohl sein, solange ich noch eine map und keine set habe.
    👍



  • schon wieder ein data race.
    hast das ja schon verbessert 😉


  • Mod

    Unnötige Kopien sollten auch noch vermieden werden:

    template <typename InputIter, typename Func>
    void Add(InputIter begin, InputIter end, Func f)
    {
        static_assert(std::is_same_v<decltype(f(*begin)), decltype(_varMap)::mapped_type>);
        decltype(_varMap) tmp;
        for ( ; begin != end; )
        {
            auto&& var = f(*begin++);
            if (_varMap.find(var->GetName()) != _varMap.end())
                return;
            tmp.try_emplace(var->GetName(), std::forward<decltype(var)>(var));
        }
        _varMap.merge(tmp);
    }
    
    template<class Iter>
    void Add(Iter first, Iter last)
    {
        Add(first, last, [](auto&& elem) -> decltype(auto) { return std::forward<decltype(elem)>(elem); });	
    }
    


  • ah ja....

    ich glaube da ist noch viel optimierungspotential in meinem Code 🙂


  • Mod

    Genaugenommen funktioniert die optimierte Variante oben sicher nur für Forward Iteratoren (oder besser).
    Falls *it ein prvalue ist (wie bei InputIteratoren möglich), führt

    auto&& var = f(*begin++);
    

    nicht zur gewünschten Lebenszeitverlängerung des temporären Objektes, falls es von f nur weitergereicht wird.

    Besser ist daher:

    template <typename InputIter, typename Func>
    void Add(InputIter begin, InputIter end, Func f)
    {
        static_assert(std::is_same_v<std::remove_reference_t<decltype(f(*begin))>, decltype(_varMap)::mapped_type>);
        decltype(_varMap) tmp;
        for ( ; begin != end; )
        {
            // if *begin is a prvalue and f(*begin) a glvalue, that glvalue might be refering to that materialized prvalue,
            // we then must copy/move the result. otherwise capture by reference.
            using var_t = std::conditional_t<std::is_reference_v<decltype(f(*begin))> && !std::is_reference_v<decltype(*begin)>,
                                             std::remove_reference_t<decltype(f(*begin))>, decltype(f(*begin))&&>;
            var_t var = f(*begin++);
            if (_varMap.find(var->GetName()) != _varMap.end())
                return;
            tmp.try_emplace(var->GetName(), std::forward<var_t>(var));
        }
        _varMap.merge(tmp);
    }
    

    Das sieht schon nicht mehr so schön aus 😞



  • @camper

    du machst mich fertig.

    Also genau genommen funtkioniert die erste optimierte Variante schon mal in meinem Fall nicht!


  • Mod

    "funktioniert nicht" ist keine Fehlerbeschreibung.



  • @camper

    "funktioniert nicht" ist keine Fehlerbeschreibung.

    du hast vollkomen recht. Sorry.
    Aber so wie es aussieht lag es nur am neu komilieren. Visual Studio hat mir beim debuggen nach dem Aufruf von

    auto var = func(*first++);

    angezeigt im watch dass var leer war

    var {...}
    

    hat sich auch nicht aufklappen lassen.


  • Mod

    Das static_assert sollte besser auch noch cv-Qualifikationen entfernen:

    static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<decltype(f(*begin))>>, decltype(_varMap)::mapped_type>);
    

  • Mod

    Was soll überhaupt dieses static_assert ? Es hat keinen Einfluss auf overload resolution und ist im Falle eines falschen Iteratortypen sowieso überflüssig.


Anmelden zum Antworten