Werte verschiedenen Types über call-by-reference tauschen



  • Hallo Leutz!

    Die main:

    int main() {
    	int a=1, b=2;
    	float x=3, y=4;
    
    	Tauschen(&a, &b);
    	Tauschen(&x, &y);
    
    	return 0;
    }
    

    Ich habe eine Funktion zum tauschen zweier Werte über call-by-reference:

    void Tauschen(int *wert1, int *wert2){
    	cout << "a/x=" << *wert1 << "| b/y=" << *wert2 << endl;
    	int tmp = *wert1;
    	*wert1 = *wert2;
    	*wert2 = tmp;
    	cout << "a/x=" << *wert1 << "| b/y=" << *wert2 << endl;
    }
    

    Aufgerufen wird die Funktion über:

    Tauschen(&a, &b)
    

    Das klappt ja alles noch wunderbar.

    Jetzt soll die Funktion aber so erweitert werden,
    dass auch die 2 float-Werte getauscht werden können.

    Das funktioniert natürlich nicht (wäre ja zu einfach -.-):

    void Tauschen(float *wert1, float *wert2){
    	cout << "a/x=" << *wert1 << "| b/y=" << *wert2 << endl;
    	float tmp = *wert1;
    	*wert1 = *wert2;
    	*wert2 = tmp;
    	cout << "a/x=" << *wert1 << "| b/y=" << *wert2 << endl;
    }
    

    Was muss ich nun tun, damit ich int- und float-werte in der Funktion tauschen kann?



  • Fishmasta schrieb:

    Das funktioniert natürlich nicht (wäre ja zu einfach -.-)

    1. "Funktioniert nicht" ist keine gute Fehlermeldung. Siehe Link in meiner Signatur.
    2. Funktionierts doch: http://ideone.com/Vn13m


  • Wieso machst du das ganze nicht mit Referenzen? Ist doch viel einfacher als mit den Zeigern.
    Zudem kannst du dir auch ein Template machen, dann kannste das ganze mit allen Datentypen erledigen.

    template< typename T >
    void swap( T& lhs, T& rhs )
    {
        std::cout << "lhs = " << lhs << " | rhs=" << rhs << std::endl;
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
        std::cout << "lhs = " << lhs << " | rhs=" << rhs << std::endl;
    }
    

    Lg freeG

    EDIT: Wobei es die Funktion auch schon fertig in der STL gibt 😉



  • pumuckl schrieb:

    Fishmasta schrieb:

    Das funktioniert natürlich nicht (wäre ja zu einfach -.-)

    1. "Funktioniert nicht" ist keine gute Fehlermeldung. Siehe Link in meiner Signatur.
    2. Funktionierts doch: http://ideone.com/Vn13m

    danach habe ich leider nicht gesucht
    die werte welche der funtion übergeben werden sollen schon als int und float und nicht beide als float deklariert sein.

    fr33g schrieb:

    Wieso machst du das ganze nicht mit Referenzen? Ist doch viel einfacher als mit den Zeigern.
    Zudem kannst du dir auch ein Template machen, dann kannste das ganze mit allen Datentypen erledigen.

    template< typename T >
    void swap( T& lhs, T& rhs )
    {
        std::cout << "lhs = " << lhs << " | rhs=" << rhs << std::endl;
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
        std::cout << "lhs = " << lhs << " | rhs=" << rhs << std::endl;
    }
    

    Lg freeG

    EDIT: Wobei es die Funktion auch schon fertig in der STL gibt 😉

    so passts 👍



  • Fishmasta schrieb:

    pumuckl schrieb:

    Fishmasta schrieb:

    Das funktioniert natürlich nicht (wäre ja zu einfach -.-)

    1. "Funktioniert nicht" ist keine gute Fehlermeldung. Siehe Link in meiner Signatur.
    2. Funktionierts doch: http://ideone.com/Vn13m

    danach habe ich leider nicht gesucht
    die werte welche der funtion übergeben werden sollen schon als int und float und nicht beide als float deklariert sein.

    so passts 👍

    Dir ist schon klar, daß dieses Template auch nur gleichartige Variablen austauchen kann 😉

    Wenn du zwei verschiedene Variablen austauschen willst, mußt du schon eine passende Überladung schreiben - allerdings hast du da das Problem, daß verschiedene Typen nur selten verlustfrei ineinander kopiert werden können.



  • Vielleicht sowas? http://ideone.com/L1Akc
    (Ja, das ist böse, ich weiß.)



  • 314159265358979 schrieb:

    Vielleicht sowas? http://ideone.com/L1Akc
    (Ja, das ist böse, ich weiß.)

    Und wenn du die (unnötigen) static_cast weglässt, dann ist es auch gleich weniger pöse:

    http://ideone.com/ntG3D



  • Naja, wenn man (in C++0x) einen explicit operator T() hat, muss man doch nen static_cast verwenden, oder?



  • 314159265358979 schrieb:

    Naja, wenn man (in C++0x) einen explicit operator T() hat, muss man doch nen static_cast verwenden, oder?

    Jain.
    Man muss einen Context verwenden in dem die Konvertierung "explicit" ist, aber das muss nicht notwendigerweise ein static_cast sein.

    Das Problem beim static_cast ist halt, dass er auch ohne mit der Wimper zu zucken einen Downcast macht. Bzw. von void* zu T* konvertiert. Was nicht unbedingt erwünscht ist.

    Wobei mir 3 Möglichkeiten einfallen das zu umgehen (und trotzdem explizite Konvertierungen zu erlauben).
    Beispiel:

    #include <iostream>
    #include <utility>
    
    struct A
    {
    	explicit A(int a) : a(a) {}
    	int a;
    };
    
    struct B
    {
    	explicit B(long b) : b(b) {}
    
    	explicit B(A const& a)
    	{
    		b = a.a;
    	}
    
    	explicit operator A() const
    	{
    		return A(b);
    	}
    
    	long b;
    };
    
    struct Base {};
    struct Derived : Base {};
    struct Unrelated {};
    
    template <class CharType, class Traits>
    std::basic_ostream<CharType, Traits>& operator << (std::basic_ostream<CharType, Traits>& stream, A const& a)
    {
    	stream << a.a;
    	return stream;
    }
    
    template <class CharType, class Traits>
    std::basic_ostream<CharType, Traits>& operator << (std::basic_ostream<CharType, Traits>& stream, B const& b)
    {
    	stream << b.b;
    	return stream;
    }
    
    // -----------------------------------
    
    template <typename T1, typename T2>
    void evil_swap(T1& a, T2& b)
    {
    	T1 tmp = std::move(a);
    	a = std::move(static_cast<T1>(b));
    	b = std::move(static_cast<T2>(tmp));
    }
    
    // -----------------------------------
    
    template <typename T1, typename T2>
    void less_evil_swap_0(T1& a, T2& b)
    {
    	T2 aAsT2(std::move(a));
    	T1 bAsT1(std::move(b));
    
    	a = std::move(bAsT1);
    	b = std::move(aAsT2);
    }
    
    // -----------------------------------
    
    template <typename D, typename S>
    D less_evil_cast_1(S&& s)
    {
    	D d(s); // NOTE: *not* return D(s), since this would allow conversion of non-related pointers and the like! (see below)
    	return d;
    }
    
    template <typename D, typename S>
    D utterly_evil_cast(S&& s)
    {
    	return D(s); // baaaaaad!
    }
    
    template <typename T1, typename T2>
    void less_evil_swap_1(T1& a, T2& b)
    {
    	T2 aAsT2(std::move(a));
    	a = less_evil_cast_1<T1>(std::move(b));
    	b = std::move(aAsT2);
    }
    
    template <typename T1, typename T2>
    void utterly_evil_swap(T1& a, T2& b)
    {
    	T1 tmp(std::move(a));
    	a = utterly_evil_cast<T1>(std::move(b));
    	b = utterly_evil_cast<T2>(std::move(tmp));
    }
    
    // -----------------------------------
    
    template <typename D, typename S>
    D less_evil_cast_2(S&& s)
    {
    	return static_cast<D>(s);
    }
    
    template <typename D, typename S>
    D less_evil_cast_2(S*&& s)
    {
    	return s; // don't allow down-casts
    }
    
    template <typename T1, typename T2>
    void less_evil_swap_2(T1& a, T2& b)
    {
    	T2 aAsT2(std::move(a));
    	a = less_evil_cast_2<T1>(std::move(b));
    	b = std::move(aAsT2);
    }
    
    int main()
    {
    	A a(42);
    	B b(6);
    
    	std::cout << a << ", " << b << std::endl;
    
    	less_evil_swap_0(a, b);
    
    	std::cout << a << ", " << b << std::endl;
    
    	less_evil_swap_1(a, b);
    
    	std::cout << a << ", " << b << std::endl;
    
    	less_evil_swap_2(a, b);
    
    	std::cout << a << ", " << b << std::endl;
    
    	// ...
    
    	Base* b1 = 0;
    	Base* b2 = 0;
    	Derived* d = 0;
    	Unrelated* u = 0;
    	void* v = 0;
    
    	less_evil_swap_0(b1, b2);
    	less_evil_swap_1(b1, b2);
    	less_evil_swap_2(b1, b2);
    
    	//less_evil_swap_0(b1, d); // doesn't compile, good
    	//less_evil_swap_1(b1, d); // doesn't compile, good
    	//less_evil_swap_2(b1, d); // doesn't compile, good
    
    	//less_evil_swap_0(b1, v); // doesn't compile, good
    	//less_evil_swap_1(b1, v); // doesn't compile, good
    	//less_evil_swap_2(b1, v); // doesn't compile, good
    
    	//less_evil_swap_0(b1, u); // doesn't compile, good
    	//less_evil_swap_1(b1, u); // doesn't compile, good
    	//less_evil_swap_2(b1, u); // doesn't compile, good
    
    	evil_swap(b1, d); // compiles, but not desirable, shit!
    	evil_swap(b1, v); // compiles, but not desirable, shit!
    	//evil_swap(b1, u); // doesn't compile, good
    
    	utterly_evil_swap(b1, d); // compiles, but not desirable, shit!
    	utterly_evil_swap(b1, v); // compiles, but not desirable, shit!
    	utterly_evil_swap(b1, u); // compiles, but not desirable, shit!
    
    	return 0;
    }
    


  • Danke für den Code, jetzt hab ichs verstanden, warum meine Variante schlecht ist. 🙂


Log in to reply