(Compiler-?)Problem mit Closures/Lazy Evaluation



  • Ich experimentiere gerade mit Closures/Lazy Evaluation. Folgender Quelltext kompiliert fehlerfrei mit dem GCC 3.2, verursacht beim VC++ .NET 2003 aber einen Fehler (siehe unten):

    class C {
    	struct Cp {
    		const C& a; const C& b;
    		Cp(const C& a_, const C& b_) : a(a_), b(b_) {} 
    		//inline operator const C() const {C r; op(a, b, r); return r;}
    	};
    
    	struct Cq {
    		const C& a; const C& b;
    		Cq(const C& a_, const C& b_) : a(a_), b(b_) {} 
    		//inline operator const C() const {C r; oq(a, b, r); return r;}
    	}; 
    public:
    	C() {}
    	C(int d) : i(d) {}
    	C& operator=(int d) {i = d; return *this;}
    
    	C(const Cp& ab) {op(ab.a, ab.b, *this);}
    	C& operator=(const Cp& ab) {op(ab.a, ab.b, *this); return *this;}
    	C(const Cq& ab) {oq(ab.a, ab.b, *this);}
    	C& operator=(const Cq& ab) {oq(ab.a, ab.b, *this); return *this;}
    
    	friend bool operator== (const C& a, const C& b) {return a.i == b.i;}
    
    	friend const Cp operator+ (const C& a, const C& b) {return Cp(a, b);} // [36]
    	friend const Cq operator* (const C& a, const C& b) {return Cq(a, b);}
    
    	friend void op(const C& a, const C& b, C& r);
    	friend void oq(const C& a, const C& b, C& r);
    private:
    	int i;
    };
    
    void foo() {
    	C a(18);
    	C b(17);
    /*	a + b;
    	C c = a+b; // kein temp. Obj.
    	a == (a+b); // temp. Obj. (T = a + b)
    	C d = b + a + b;  // temp. Obj. + opt. (T = b + a, d = T + b)*/ // OK
    
    	a + b * a == a; // OK
    	a + b + b * a == a; // nicht mit VC++ .NET 2003 [54]
    }
    
    ztest.cpp(54) : error C3767: '+' auf übereinstimmende Funktion kann nicht zugegriffen werden
            kann die friend-Funktion bei 'ztest.cpp(36)' sein: '+' [wird möglicherweise mit der argumentbezogenen Suche gefunden]
    ztest.cpp(54) : error C2676: Binärer Operator '+': 'const C::Cp' definiert diesen Operator oder eine Konvertierung in einen für den vordefinierten Operator geeigneten Typ nicht
    

    Meine Frage: Welcher der Compiler verhält sich hier standardkonform und wie muss ich meinen Quelltext ändern, dass er mit beiden kompiliert?



  • Du definierst eine private struct Cp und einen public-Operator, der ein struct Cp zurückgibt?



  • unbekannt schrieb:

    Du definierst eine private struct Cp und einen public-Operator, der ein struct Cp zurückgibt?

    Hat mich auch gewundert, dass das geht. Wenn ich C::Cp aber public mache, ändert das nichts.



  • Hallo,
    also ich denke der Code ist legal, aber tricky.

    a + b + b * a == a; 
    ^^^^^   ^^^^^
      |      |
    C::cp   C::cq
    

    Du hast also: C::cp + C::cq. Du brauchst also einen operator+, der C::cp und
    ein C::cq akzeptiert.
    Du hast aber nur einen friend-operator+, der zwei Cs akzeptiert. Soweit kein Problem, da C benutzerdefinierte Konvertierung sowohl für C::cp nach C, als auch für C::Cq nach C.
    Jetzt kommt aber der Trick:
    friend-Funktionen werden nur über Argument-Dependent Lookup (ADL) in den "enclosing scope" injeziert. Das heißt, sie sind nur sichtbar, wenn die Klasse, mit der sie befreundet sind über ADL mit zu der zu durchsuchenden Scope-Liste gehört.
    Das heißt, der Compiler muss ADL unterstützen, den Scope C::cp, C::cq sowie den umschließenden Scope C mit berücksichtigen, dadurch die friend-Funktion in den globalen Namespace injezieren und diese dann nach Umwandlung der Parameter aufrufen.

    Der VC scheint dies nicht zu können.



  • also called Koenig lookup



  • Vielen Dank!
    Hast du eine Idee für einen Workaround für den VC++?
    EDIT: Abgesehen von

    a + b + C(b * a) == a;
    


  • 🙂



  • Hast du eine Idee für einen Workaround für den VC++?

    Ne. Ich beherrsche C++ leider nicht gut genug um mir Workarounds auf dem Papier ausdenken zu können. Dafür brauche ich immer den entsprechenden Compiler zum Testen. Und da ich den neuen VC++ nicht besitze, wird das nichts.



  • Könnte mir jemand freundlicherweise erläutern, was der Code mit fauler Auswertung oder Closures zu tun hat. Ich versuche das schon seit Threadbeginn herauszufinden, komme aber nicht wirklich weiter -- Vielen Dank.



  • Die + und * Operatoren liefern erstmal sowas wie Thunks zurück, C + C => Cp und C * C => Cq, die erst bei Bedarf wieder in C konvertiert werden. Closures seh ich allerdings nirgends, ich bezweifle auch, dass es sowas in C++ geben kann.



  • Ah, jetzt habe ich mir das mal aufgemalt. Die eigentliche Auswertung findet erst dann statt, wenn die C? nach C konvertiert werden, wobei dann die o?-Funktionen wirklich 'arbeiten'. Chique.



  • Bashar schrieb:

    Closures seh ich allerdings nirgends, ich bezweifle auch, dass es sowas in C++ geben kann.

    Warum nicht? Was versteht man unter Closures? Ich beziehe mich darauf:

    B. Stroustrup, TC++PL §22.4.7 schrieb:

    Das von * erzeugte Objekt ist in etwa das, was man im technischen Umfeld auch als Abschluß (englisch: closure) nennt.



  • Ja, was sind eigentlich Closures? Im Wesentlichen Bezüge ('Referenzen') auf (unbenannte?) Funktionen, wobei deren 'Zustand' (bzw der Zustand ihrer Argumente) erhalten bleibt. Oder so ähnlich :).
    Stroustrup meint http://www.research.att.com/~bs/glossary.html#Gclosure



  • Stroustrup meint damit etwa sowas:

    struct Closure {
      T1 operator()(T2,  ....) {
        // ...
      }
      Closure(....): initialisiererliste {... }
    private:
      // ein paar Daten
    };
    

    wobei die privaten Daten beim Erstellen des Objektes gesetzt werden. Für echte Closures müßte der Compiler aber selbst erkennen, welche Variablen der operator() benutzt. Diese Technik ist vor allem in funktionalen Sprachen gebräuchlich, besonders zusammen mit anonymen Funktionen.

    Ohne diesen Automatismus würd ich nicht von Closures reden. Man kann aber trotzdem in C++ in eingeschränktem Maße und umständlich funktional programmieren. Siehe z.B. den Header <functional>, oder boost::spirit.



  • Man kann aber trotzdem in C++ in eingeschränktem Maße und umständlich funktional programmieren. Siehe z.B. den Header <functional>, oder boost::spirit

    Oder boost::mpl oder FC++


Anmelden zum Antworten