Referenzen



  • Hi Leute,

    ich habe ein Problem mit einer Funktion und Referenzen.

    So sieht die Funktion aus:

    void g(int& i) {
    	std::cout << i << std::endl;
    }
    

    Wenn ich in der Main-Funktion nun folgendes mache:

    g(50);
    

    funktioniert dies nicht. Mir ist verständlich warum: 50 liegt nicht im Speicher (kein lvalue), weswegen eine Referenz darauf nicht zulässig ist, es sei denn sie ist const (schreibgeschützt sozusagen).

    Deswegen mache ich es wie folgt:

    int i = 50;
    g(i);
    

    i liegt im Speicher und kann daher by reference übergeben werden.
    Nun will ich einen unsigned int auf die selbe Weise übergeben:

    unsigned int i = 50;
    g(i);
    

    Dieses Codestück schlägt fehl 😞
    Compilermeldung:

    cannot convert parameter 1 from 'unsigned int' to 'int &'
    

    Woran liegt das? i liegt doch im Speicher, deswegen wurde es extra vorher definiert.
    Ist der reservierte Speicher zu klein, weswegen keine implizite Konvertierung möglich ist? Aber unsigned int hat doch genauso viele Byte wie ein normaler (signed) int, oder?

    Help 😃

    Vielen Dank!

    PS: Ich weiß, diese Beispiele haben keinen praktischen Nutzen, aber sie stehen nur symbolisch für dieses Problem.



  • In

    void g(int& i) {
        std::cout << i << std::endl;
    }
    

    hast du ja int& (also referenz auf int) definiert.
    Da kannst du jetzt kein unsigned int reintun.
    Also um ein unsigned int an die Funktion übergeben zu können musst du die funktion auf

    void g(unsigned int& i) {
        std::cout << i << std::endl;
    }
    

    ändern.

    Wenn du beides haben willst kannst du ja die Funktion überladen also so:

    //Funktion 1
    void g(int& i) {
        std::cout << i << std::endl;
    }
    //Funktion2
    void g(unsigned int& i) {
        std::cout << i << std::endl;
    }
    
    //Aufrufe
    unsigned int i1=1;
    int i2=1;
    g(i1);//-->Funktion1
    g(i2);//-->Funktion2
    

    Hoffe das hilft dir!!!
    Mfg Karldin



  • Mist fehler zum AUFRUF :
    DAS RICHTIGE:

    //Aufrufe
    unsigned int i1=1;
    int i2=1;
    g(i1);//-->Funktion2
    g(i2);//-->Funktion1
    


  • @Karldin Shinowa: Was du geschrieben hast, weiß thomassteiner auch. Er hat ja auch nach was ganz anderem gefragt 😉



  • Danke für die Antwort, aber wie bereits nett erwähnt, wusste ich das selbst *g*

    Ich muss den Grund erforschen, warum ich hier keinen unsigned int übergeben kann.

    Vorschläge?



  • thomassteiner schrieb:

    Aber unsigned int hat doch genauso viele Byte wie ein normaler (signed) int, oder?

    Ja das stimmt schon. Aber man macht es ja unsigned, damit es keine Negativen Werte bekommt. Und wenn du dann ein unsigned int an ein int& übergeben könntest könntest du ja auch dann negative werte in den int reinschreiben.

    Obwohl es ja unsigned ist. Siehe es einfach als wären es zwei Grundverschiedene Datentypen sind und es deswegen nicht funktioniert 😃



  • Ob negativ oder nicht resultiert doch nur aus der Interpretation des höchsten Bits wenn ich mich erinnere. Wenn du in einen unsigned int über irgendwelche pointer was negatives reinschreibst, und dann wieder über den unsigned int zugreifst, dann dürftest du einfach den Wert+(max_int/2) haben. Der compilererror wird aber vermutlich aus deinem grund auftreten 🙂



  • Freak_Coder schrieb:

    Siehe es einfach als wären es zwei Grundverschiedene Datentypen

    Es _sind_ zwei verschiedene Datentypen, zwar verwandt, trotzdem verschieden. Und es gibt keine implizite Umwandlung von Referenzen, da Referenzen nunmal an Objekte gebunden sind. Dh, es müsste schon das Objekte selbst implizit umgewandelt werden, was der Compiler natürlich nicht automatisch macht. Das musst du ihm schon selber sagen.

    void foo(signed int& x)
    {
        //....
    }
    void bar(unsigned int& x)
    {
        signed int tmp = x;
        foo(tmp);
        x = tmp;
    }
    

    Eine andere Möglichkeit wäre, dir einen reference_cast zu basteln.

    #include <iostream>
    
    template <class T>
    struct reference_info;
    
    template <class T>
    struct reference_info<T&>
    {
    	typedef T native_type;
    };
    
    template <class To, class From>
    To reference_cast(From& x)
    {
    	return *reinterpret_cast<typename reference_info<To>::native_type*>(&x);
    }
    
    int main()
    {
    	signed int a = -1;
    	unsigned int b = 1;
    	signed int& ra = a;
    	unsigned int& rb = b;
    	std::cout << ra << std::endl;
    	std::cout << rb << std::endl;
    	std::cout << reference_cast<unsigned int&>(ra) << std::endl;
    	std::cout << reference_cast<signed int&>(rb) << std::endl;
    	return 0;
    }
    

    Nur ob das sinnvoll ist, wage ich zu bezweifeln. Zudem müssten in die Funktion noch ein paar static asserts, da sie momentan zu unsicher ist.



  • Freak_Coder schrieb:

    Ja das stimmt schon. Aber man macht es ja unsigned, damit es keine Negativen Werte bekommt. Und wenn du dann ein unsigned int an ein int& übergeben könntest könntest du ja auch dann negative werte in den int reinschreiben.

    Obwohl es ja unsigned ist. Siehe es einfach als wären es zwei Grundverschiedene Datentypen sind und es deswegen nicht funktioniert 😃

    Die Referenz auf einen const int funktioniert aber sehr wohl. Der Übersetzer konvertiert den unsigned int auf int und legt eine temporäre Variable im Speicher ab, auf der er arbeitet.
    Die Konvertierung von unsigned int muss also möglich sein, da einem das const sonst auch nichts mehr bringen würde.

    unsigned int = 50;
    g(50); //funktioniert nicht
    h(50); //funktioniert
    void g(int& i);
    void h(int const& i);
    

    Wenn der Übersetzer also unsigned int auf int konvertieren kann, warum tut er das bei g nicht auch? Die Größe des Objektes im Speicher ist schließlich gleich? Da müsste es doch möglich sein eine temporäre Variable im Speicher anzulegen (so wie er es bei const auch tut) und auf dieser zu Arbeiten?

    Natürlich gehen die Änderungen nachher verloren, wenn die Lebensdauer der temporären Variable mit g endet, aber das macht ja nichts.
    Oder verhindert der Übersetzer diesen Aufruf einfach, weil man sonst fälschlicherweise glauben könnte, dass die Änderungen beibehalten werden, und somit Fehler entstehen?

    Gute Vermutung, oder :D?



  • Wie du sagtest, bei "const int&" kann der Compiler eine temporäre Variable anlegen, auf die er die Referenz biegt - bei "int&" geht das nicht, da die Funktion den übergebenen Parameter schreiben könnte, also braucht der Compiler eine exakte Typ-Übereinstimmung.



  • So, hab mal etwas im Standard gelesen und bin dabei auf 2 interessante Stelle gestossen:

    — Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [Example:
    double& rd2 = 2.0; // error: not an lvalue and reference not const
    int i = 2;
    double& rd3 = i; // error: type mismatch and reference not const
    —end example]

    Du musst also eine non-volatile const Referenz haben, damit die implizite Umwandlung funktioniert. ZB

    int a = 1;
    int& ra = a;
    unsigned int& rua = ra; // nope
    const unsigned int& rca = ra; // ok
    

    Warum das so ist, kannst du dir auch beantworten. Stell dir mal folgendes Szenario vor:

    void add_something(float& x)
    {
        x += 1.25;
    }
    
    int a = 2;
    int& ra = a;
    add_something(ra);
    cout << ra << endl;
    

    Wenn sowas möglich wäre, dann wird hier ein Wert ausgegeben, der nicht dem Erwarteten entspricht. Das wäre ein grosser Eingriff an der Typsicherheit und könnte ziemlich katastrophal werden.

    An lvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. That is, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type. No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.67)

    Der reference_cast ist also unnötig, sowas funktioniert bereits mit reinterpret_cast, wo im Grunde das gleiche gemacht wird. Wenn du also eine entsprechende Referenz brauchst, dann gib das explizit an.

    void g(int& i) {
        std::cout << reinterpret_cast<unsigned int&>(i) << std::endl;
    }
    

    Wenn du mehr Infos brauchst, dann empfehle ich dir Abschnitt 13.3.3.1 Implicit conversion sequences.


Anmelden zum Antworten