C++17 hat Concepts


  • Mod

    @volkard: 😃 😃 😃
    https://www.c-plusplus.net/forum/p2158260#2158260

    volkard schrieb:

    Nexus schrieb:

    krümelkacker schrieb:

    und dann ist auto ja auch nur ein Typ-Platzhalter, der entsprechend dekoriert werden kann:

    vector<auto> blah = myfunction();
    

    Ah, das kannte ich ebenfalls nicht. Interessant! 💡

    Naja, gcc kennt das auch nicht.
    Ist es wirklich erlaubt?

    Ja.

    Nathan schrieb:

    Hab die aktuelle Syntax jetzt nicht im Kopf

    So:

    template<typename T>
    concept bool LessThanComparable = requires (T a, T b) {{a < b} -> bool;}
    

    Kann ich dem Concept sagen,dass type T eine function haben muss, mit einer entsprechenden Signatur oder ist das Ganze beschränkt, wie im obigen Beispiel
    auf Operatoren?

    template <typename T>
    concept bool C = requires (T a, int i, float b) {
        {a.function(i, b)} -> std::string;
    };
    


  • Arcoth schrieb:

    Kann ich dem Concept sagen,dass type T eine function haben muss, mit einer entsprechenden Signatur oder ist das Ganze beschränkt, wie im obigen Beispiel
    auf Operatoren?

    template <typename T>
    concept bool C = requires (T a, int i, float b) {
        {a.function(i, b)} -> std::string;
    };
    

    Wobei das jetzt nicht ganz genau die Signatur prüft. Die Memberfunktion könnte auch als std::string function(float i, float b) deklariert worden denke ich. Schließlich prüft requires nur ob der Ausdruck wohlgeformt ist, aber die Konvertierung int -> float kann implizit passieren (wie noch viele weitere Konvertierungen).


  • Mod

    sebi707 schrieb:

    Wobei das jetzt nicht ganz genau die Signatur prüft.

    Das ist mir klar. Es macht nur einfach nie Sinn die genaue Signatur einer Methode zu prüfen.


  • Mod

    Na gut, man kann natürlich ganz rustikal anpacken:

    template <typename T>
    concept bool HasF = requires {
    	{&T::f} -> void (T::*)(int);
    };
    

    Ist aber wie gesagt nicht sonderlich nützlich, weil man ja i.d.R. die Funktion aufrufen und nicht per Funktionszeiger irgendwo speichern will.



  • Arcoth schrieb:

    Na gut, man kann natürlich ganz rustikal anpacken [...]

    Ah nett. Darauf bin ich jetzt gerade nicht gekommen...



  • Ich hab mir mal den gcc trunk compiliert und etwas mit concepts rumgespielt und bin auf etwas merkwürdiges gestoßen:

    int foo(int x)
    {
            return 0;
    }
    
    template <typename T>
    concept bool C = requires (T x) {
            {foo(x)} -> int&;
    };
    
    void bar(C x)
    {
            //int& y = foo(x);
    }
    
    int main()
    {
            bar(5);
    }
    

    Ich hätte hier erwartet, dass mein Concept C nicht erfüllt ist weil foo keine Referenz zurück gibt. Stattdessen gibts dann erst einen Fehler wenn ich die Zuweisung int& y = foo(x); auskommentiere. Habe ich etwas falsch verstanden oder wieso funktioniert das nicht?

    14.10.1.5 Implicit conversion constraints schrieb:

    An implicit conversion constraint is a constraint that specifies a requirement on the implicit conversion of an expression E to a type T.


  • Mod

    Ja, das ist ein Bug. Reporte mal.


  • Mod

    Huch. Ich glaub' GCC hat nicht mehr alle Tassen im Schrank:

    int foo(int x) 
    { 
            return x; 
    } 
    
    template <typename T> 
    concept bool C = requires (T x) { 
            {foo(x)} -> void; 
    }; 
    
    static_assert( C<int> );
    

    Das kompiliert gar. Merkwürdig. Dabei ist gar nichts implizit zu void konvertierbar..



  • Arcoth schrieb:

    Reporte mal.

    Erledigt: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67240



  • Arcoth schrieb:

    template<typename T>
    concept bool LessThanComparable = requires (T a, T b) {{a < b} -> bool;}
    

    Mal ne dumme Frage, die Syntax war doch mal etwa so (wenn ich mich richtig erinnere)

    template <typename T>
    concept bool LessThanComparable
    {
        bool operator<(T a, T b); // T braucht einen operator< der 2 T's nimmt und bool zurückgibt
    }
    

    Wieso ist das jetzt so "häßlich" mit =requires und Pfeil für den Rückgabewert? Das concept Schlüsselwort sollte doch ausreichen um dem Compiler zu sagen dass jetzt die requirements für T kommen, gibts einen bestimmten Grund warum man genau die Syntax gewählt hat?



  • happystudent schrieb:

    Wieso ist das jetzt so "häßlich" mit =requires und Pfeil für den Rückgabewert? Das concept Schlüsselwort sollte doch ausreichen um dem Compiler zu sagen dass jetzt die requirements für T kommen, gibts einen bestimmten Grund warum man genau die Syntax gewählt hat?

    Die neue Version ist allgemeiner. Das alte sagt lediglich, dass der operator < (a, b) vorhanden ist und bool zurueckgibt. Das neue hingegen sagt, dass diue expression a<b gueltig ist und das Ergebnis in boolean umgewandelt werden kann, quasi dass

    bool c = a<b
    

    gueltig ist.
    Mehrere requires koennen wahrscheinlich auch mit logischen operatoren verbunden werden.

    Ausserdem sieht es vermutlich besser aus, wenn man nicht alles in einer Zeile schreibt:

    template<typename T>
    concept bool LessThanComparable =
    requires (T a, T b) {
        {a < b} -> bool;
    }
    


  • Marthog schrieb:

    Die neue Version ist allgemeiner. Das alte sagt lediglich, dass der operator < (a, b) vorhanden ist und bool zurueckgibt. Das neue hingegen sagt, dass diue expression a<b gueltig ist und das Ergebnis in boolean umgewandelt werden kann, quasi dass

    bool c = a<b
    

    gueltig ist.

    Ok, also würde das concept sowohl für eine freie operator-Funktion bool operator<(T a, T b) als auch für eine Member-operator-funktion bool T::operator<(T b) funktionieren? Das ist dann natürlich ein Vorteil der Schreibweise 👍


  • Mod

    Marthog schrieb:

    Das neue hingegen sagt, dass diue expression a<b gueltig ist und das Ergebnis in boolean umgewandelt werden kann, quasi dass

    bool c = a<b
    

    gueltig ist.

    Nicht Quasi, sondern genau das.

    Mehrere requires koennen wahrscheinlich auch mit logischen operatoren verbunden werden.

    Ja, siehe §14.10.1.1.

    Ausserdem sieht es vermutlich besser aus, wenn man nicht alles in einer Zeile schreibt:

    Seh' ich nicht so.



  • Ist im aktuellen Concepts Stand auch etwas vorgesehen mit dem man Concepts für bestimmte Typen "zurechtbiegen" kann?

    Also wenn ich einen Typ X habe, wo x1 < x2 eben nicht compiliert, und ich den trotzdem in einem LessThanComparable -fordernden Template verwenden will.
    Kann ich dann eine "Spezialisierung" von LessThanComparable für X schreiben, oder muss ich wirklich "global" dafür sorgen dass x1 < x2 eben doch funktioniert?


  • Mod

    hustbaer schrieb:

    Ist im aktuellen Concepts Stand auch etwas vorgesehen mit dem man Concepts für bestimmte Typen "zurechtbiegen" kann?

    Also wenn ich einen Typ X habe, wo x1 < x2 eben nicht compiliert, und ich den trotzdem in einem LessThanComparable -fordernden Template verwenden will.
    Kann ich dann eine "Spezialisierung" von LessThanComparable für X schreiben, oder muss ich wirklich "global" dafür sorgen dass x1 < x2 eben doch funktioniert?

    Macht IMO keinen Sinn. Sahen die Verfasser des Proposals ähnlich:

    §7.1.7/7 schrieb:

    A program shall not declare an explicit instantiation (14.8.2), an explicit specialization (14.8.3), or a partial specialization of a concept definition. [ Note: This prevents users from subverting the constraint system by providing a meaning for a concept that differs from its original definition.end note ]



  • Also ich meine das was in der alten Concepts Version (die ursprünglich für IIRC C++11 vorgesehen war) mit Concept-Maps möglich war:

    concept Stack<typename X> {
        typename value_type;
        void push(X&, const value_type&);
        void pop(X&);
        value_type top(const X&);
        bool empty(const X&);
    };
    
    template<typename T> concept_map Stack<std::vector<T>> {
        typedef T value_type;
        void push(std::vector<T>& v, const T& x) { v.push_back(x); }
        void pop(std::vector<T>& v) { v.pop_back(); }
        T top(const std::vector<T>& v) { return v.back(); }
        bool empty(const std::vector<T>& v) { return v.empty(); }
    };
    

    Hier entfernt bzw. verändert die Concept-Map ja nicht die Constraints, sie definiert lediglich wie bestimmte geforderte Operationen bei einem bestimmten Typ funktionieren.

    Und das macht mMn. schon Sinn.


Anmelden zum Antworten