UnaryFunction in c++ 17


  • 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.


  • Mod

    Arcoth schrieb:

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

    Hatte ich doch schon eine Seite vorher erklärt? Placebo um sich besser zu fühlen.



  • Ja aber das problem ist doch auch dass ich am Kompilierungsfehler nicht sehe wer der Aufrufer ist der den Fehler verursacht.

    Gebe ich zum beispiel nun einen iterator von strings in die Funktion Add erhalte ich den Fehler:

    GetName': is not a member of 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'

    hier ist kein Bezug zum Aufrufer!



  • Verstehe ich das richtig enable if ist dafür da dass ich z.B. zwischen zwei Überladungen steuern will.

    wenn ich z.b. einmal einen string oder einen shared_ptr<MyVariable> als Iter typ rein geben will

    template <typename Iter, typename = std::enable_if_t<std::is_same_v<string>, std::iterator_traits<Iter> >>
    void Add(Iter first, Iter last);
    
    template <typename Iter, typename = std::enable_if_t<std::is_same_v<shared_ptr<MyVariable>, std::iterator_traits<Iter>>>>
    void Add(Iter first, Iter last);
    

    Wenn ich das aber mache erhalte ich den Fehler dass Add schon deklariert ist.
    Ich mache wahrscheinlich genau den Fehler der hier beschrieben ist

    http://en.cppreference.com/w/cpp/types/enable_if

    "A common mistake is to declare two function templates that differ only in their default template arguments"

    Aber irgendwie weiß ich jetzt nicht wie anders machen.


Anmelden zum Antworten