UnaryFunction in c++ 17



  • muss es nicht

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>, int> = 0>
    

    heißen?

    also typename vor std::enable_if_t weg
    dafür aber typename vor std::iterator_traits hin?


  • Mod

    booster schrieb:

    muss es nicht

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>, int> = 0>
    

    heißen?

    also typename vor std::enable_if_t weg
    dafür aber typename vor std::iterator_traits hin?

    richtig.



  • ich raff das ganze noch nicht

    wieso im Fall "string" int als parametertyp
    und im Fall shared_ptr<MyVariable> long?

    wenn ich nun deinen Code hernehme und folgendes aufrufe:

    set<string> names = {"Var1", "Var2" } ;
    Add(names.begin(), names.end());
    

    erhalte ich den Fehler:
    "no overloaded function takes 2 arguments"



  • sorry. das war falsch. jetzt würde ich den letzen Beitrag auch löschen wenn ich könnte 🙂

    Mache ich folgendes:

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>, int> = 0>
            void Add(Iter first, Iter last)
    {
         // mach nicht viel sinn aber fürs beispiel halt.
         const std::set<string> names(begin(first), end(last));
    }
    
    set<string> names = {"Var1", "Var2" } ;
    Add(names.begin(), names.end());
    

    erhalte ich den Fehler

    'begin': no matching overloaded function found

    Weil er jetzt davon ausgeht das Iter vom typ int ist ?!?

    ändere ich nun int in string

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>, string> = 0>
            void Add(Iter first, Iter last)
    

    erhalte ich den vorher besagten Fehler
    "no overloaded function takes 2 arguments"


  • Mod

    booster schrieb:

    wieso im Fall "string" int als parametertyp
    und im Fall shared_ptr<MyVariable> long?

    Gewohnheit. iirc hatte gcc früher mal Probleme die Namen von Spezialisierungen unterschiedlich zu mangeln, und so die Templates auseinanderzuhalten, wenn enable_if stets den gleichen Typ liefert.


  • Mod

    const std::set<string> names(begin(first), end(last));
    

    Was soll begin(first) sein?
    first ist doch schon ein Iterator.



  • Was soll begin(first) sein?

    Äh natürlich.

    const std::set<string> names(first, last);
    

    dann machst mehr Sinn 🙂

    iirc hatte gcc früher mal Probleme die Namen von Spezialisierungen unterschiedlich zu mangeln, und so die Templates auseinanderzuhalten, wenn enable_if stets den gleichen Typ liefert.

    ist das nicht das Problem auf das ich schon hingewiesen habe?
    "A common mistake is to declare two function templates that differ only in their default template arguments"

    Aber nochmals. Du setzt hier einfach 2 verschiedene typen dass das Template richtig erkannt wird?

    Wieso funktioniert dann statt int nicht auch string bzw. im anderen Fall shared_ptr<MyVariable>


  • Mod

    booster schrieb:

    Aber nochmals. Du setzt hier einfach 2 verschiedene typen dass das Template richtig erkannt wird?

    Du kannst gerne immer int nehmen, hier geht es um ein Problem, das in modernen Compilern schon lange gelöst ist.

    booster schrieb:

    ist das nicht das Problem auf das ich schon hingewiesen habe?
    "A common mistake is to declare two function templates that differ only in their default template arguments"

    meint dieses:

    //        Parameter   Parameter     Defaultargument
    //        --------    --------   ------------------------
    template <typename T, typename = std::enable_if_t<abc...>>
    void f(T);
    
    //        Parameter   Parameter     Defaultargument
    //        --------    --------   ------------------------
    template <typename T, typename = std::enable_if_t<xyz...>>
    void f(T);
    

    Die Templateparameterlisten beider Deklarationen sind gleich (jeweils <typename,typename>), ebenso die Funktionsparameter, mithin sind die Signaturen beider Templates gleich und eine Überladung ist nicht möglich. Die Tatsache, dass die Defaultargumente unterschiedlich sind, spielt keine Rolle.

    Das ist ganz Analog zu Defaultfunktionsargumenten:

    void f(int x, int y = 1);
    void f(int x, int y = 1); // error defaultargument nochmals angegeben
    void f(int x, int y = 2); // error defaultargument nochmals angegeben und ausserdem anders
    


  • aha. ja dann brauche ich ja aber auch kein int angeben. dann kann ich ja auch void nehmen und das ist sowieso schon default?

    also so?

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>> = 0>



  • aber dann erhalte ich wieder den Fehler. "no overload excepts 2 params"
    nur mit int gehts???


  • Mod

    booster schrieb:

    aha. ja dann brauche ich ja aber auch kein int angeben. dann kann ich ja auch void nehmen und das ist sowieso schon default?

    also so?

    template <typename Iter, std::enable_if_t<std::is_same_v<std::string, typename std::iterator_traits<Iter>::value_type>> = 0>

    void ist kein Objekttyp, und kann deshalb nicht der Typ eines Templatearguments sein.

    void x = void{};
    

    geht ja auch nicht.



  • Klingt logisch 🙂

    Jetzt hänge ich nur noch an deiner Aussage "in den meisten Fällen reicht eine constexpression.

    Eine if else abfrage ist einer Überladung vorzuziehen. Echt jetzt?


  • Mod

    booster schrieb:

    Klingt logisch 🙂

    Vor C++11 hat man auch gerne void* eingesetzt:

    template <typename T>
    void foo(T, typename enable_if<condition>::type*=0);
    

    Allerdings haben wir jetzt nullptr, und dieses hier zu verwenden führt zu mehr Schreibaufwand als int.

    booster schrieb:

    Eine if else abfrage ist einer Überladung vorzuziehen. Echt jetzt?

    Echt jetzt.
    if constexpr wertet einen konstanten Ausdruck aus und instantiiert nur den Zweig der Bedingung, der auch tatsächlich ausgeführt wird. Damit lassen sich u.a. unötige Spezialisierungen und Codeduplikation vermeiden.
    Klassisch:

    template <int I>
    int fak() {
        if (I==0)
            return 1;
        else
            return I*fak<I-1>();
    }
    ...
    fak<0>();
    

    Das kompiliert nicht. Obwohl der Compiler weiss, dass für I==0 die Alternative I*fak<I-1>() nicht ausgeführt wird, muss dieser Teil trotzdem instantiiert werden. Was zu unbegrenzter rekursiver Instantiierung führen muss.
    Deshalb musste hier bisher statt dessen spezialisiert werden.
    Mit if constexpr können wir einfach schreiben

    template <int I>
    int fak() {
        if constexpr (I==0)
            return 1;
        else
            return I*fak<I-1>();
    }
    

    und sind fertig.


Anmelden zum Antworten