Vektorrechnung - Schnittpunkt zweier Vektoren



  • Hi!

    Meine Frage ist eigentlich eher mathematischer Natur.
    Ich habe 4 Vektoren v1(-4,5), v2(-2,-5), v3(-6,2), v4(5,4),
    welche 2 Linien bilden l1(v1, v2), l2(v3, v4).

    Ich möchte nun den Schnittpunkt der beiden Linien berechnen, auf dem Papier bekomme ich das auch noch hin, wenn ich es mit einem linearen Gleichungssystem löse. Der Schnittpunkt ist S(-3 28/57 ; 2 26/57).
    Ich will das Problem aber vektoriell lösen, komme damit aber nicht zurecht.

    Hier mal mein Ansatz:

    class CVektor  
    {
    public:
    	CVektor();
    	CVektor(int x, int y);
    	CVektor(POINT pt);
    	virtual ~CVektor();
    	CVektor operator+(CVektor val);
    	CVektor operator-(CVektor val);
    	CVektor operator*(double val);
    	bool IsCollinear(CVektor val);
    	double GetDeterminant(CVektor val);
    private:
    	double m_x;
    	double m_y;
    };
    
    CVektor::CVektor()
    {
    	m_x = 0;
    	m_y = 0;
    }
    
    CVektor::CVektor(int x, int y)
    {
    	m_x = x;
    	m_y = y;
    }
    
    CVektor::CVektor(POINT pt)
    {
    	m_x = pt.x;
    	m_y = pt.y;
    }
    
    CVektor::~CVektor()
    {
    
    }
    
    CVektor CVektor::operator+(CVektor val)
    {
    	CVektor res;
    	res.m_x = m_x + val.m_x;
    	res.m_y = m_y + val.m_y;
    	return res;
    }
    
    CVektor CVektor::operator-(CVektor val)
    {
    	CVektor res;
    	res.m_x = m_x - val.m_x;
    	res.m_y = m_y - val.m_y;
    	return res;
    }
    
    CVektor CVektor::operator*(double val)
    {
        CVektor res;
    	res.m_x = m_x * val;
    	res.m_y = m_y * val;
        return res;
    }
    
    bool CVektor::IsCollinear(CVektor val)
    {
    	if(this->GetDeterminant(val) == 0) return true;
    	else return false;
    }
    
    double CVektor::GetDeterminant(CVektor val)
    {
    	return m_x * val.m_y - m_y * val.m_x;
    }
    
    class CLinie  
    {
    public:
    	CLinie();
    	CLinie(CVektor v1, CVektor v2);
    	virtual ~CLinie();
    	CVektor GetSchnittpunkt(CLinie val);
    	bool IsParallel(CLinie val);
    	bool IsEqual(CLinie val);
    	CVektor m_v1;
    	CVektor m_v2;
    };
    
    CLinie::CLinie()
    {
    	m_v1 = CVektor(0, 0);
    	m_v2 = CVektor(0, 0);
    }
    
    CLinie::CLinie(CVektor v1, CVektor v2)
    {
    	m_v1 = v1;
    	m_v2 = v2;
    }
    
    CLinie::~CLinie()
    {
    
    }
    
    CVektor CLinie::GetSchnittpunkt(CLinie val)
    {
    	CVektor res, v_min, v_mul;
    	v_min = val.m_v1 - m_v1;
    	v_mul = val.m_v2 * -1;
    	double D_s = v_min.GetDeterminant(v_mul);
    	double D = m_v2.GetDeterminant(v_mul);
    	double s = D_s / D;
    	//Ich weiß, dass ich hier noch prüfen müsste ob die beiden parallel liegen, weil sonst eine Division durch 0 auftritt.
    	res = m_v1 + (m_v2 * s);
    	return res;
    }
    
    bool CLinie::IsParallel(CLinie val)
    {
    	return m_v2.IsCollinear(val.m_v2);
    }
    
    bool CLinie::IsEqual(CLinie val)
    {
    	return this->IsParallel(val) && m_v2.IsCollinear(val.m_v1 - m_v1);
    }
    

    Falls ihr euch fragt, wie ich immerhin zu diesem code gekommen bin, obwohl ich von Vektorrechnung keine Ahnung habe... Ich habe mich daran orientiert: http://velociraptor.mni.fh-giessen.de/Programmierung/progI-html-dir/node9.html 🙂

    Wenn ich das dann probiere

    CVektor v1(-4,5), v2(-2,-5), v3(-6,2), v4(5,4), erg;
    CLinie l1(v1, v2), l2(v3, v4);
    
    erg = l1.GetSchnittpunkt(l2);
    

    wird der Ergbenisvektor (-4,8235 ; 2,9412), was nicht mit meiner Lösung aus dem LGS übereinstimmt. 😞



  • wchristian schrieb:

    Ich möchte nun den Schnittpunkt der beiden Linien berechnen [...] Ich will das Problem aber vektoriell lösen, komme damit aber nicht zurecht.

    In 2D gibt es einen tollen Trick. Du kannst sowohl Geraden als auch Punkte mit 3D-Vektoren beschreiben und ganz einfach mit diesen Vektoren rechnen. Stell Dir dazu folgendes in 3D vor:

    /|
                       / |
      (0;0;0)         |  |
         *            |  |
     (Ursprung)       |  |
                      | /
                      |/ (Ebene "z=1")
    

    Die 2D-Ebene, in der Du die Punkte und Linien beschreibst, ist die "z=1"-Ebene.

    Einen Punkt repräsentierst Du durch einen 3D-Vektor. Stell Dir vor, wie er vom Ursprung ausgehend, in eine Richtung zeigt und damit eine Gerade in 3D definiert. Diese Gerade schneidet die z=1 Ebene in dem Punkt, den du beschreiben willst. Der zum Punkt (x;y) gehörige 3D-Vektor ergibt sich einfach durch anhängen einer 1: (x;y;1.0).

    Eine Gerade repräsentierst Du auch durch einen 3D-Vektor. Stell Dir dazu eine zweite Ebene vor, die durch den Ursprung geht und die z=1 Ebene schneidet. Der Schnitt zweier Ebenen ist die Gerade. Diese zweite Ebene kannst Du auch mit einem 3D-Vektor beschreiben, nämlich den Normalvektor, also der, der senkrecht auf der Ebene steht.

    Der Trick ist nun der, dass das Kreuzprodukt dazu benutzt werden kann, aus 2 Punkten eine Linie zu erzeugen und aus 2 Linien einen Schnittpunkt zu berechnen:

    linie1   = cross( punkt1, punkt2 );  
      linie2   = cross( punkt3, punkt4 );  
      schnittp = cross( linie1, linie2 );
    

    wobei punkt1..4 und linie1..2 3D-Vektoren sind (Homogene Koordinaten) und "cross" das Kreuzprodukt berechnet. Warum funktioniert das? Naja, die Punkte werden durch 3D-Richtungsvektoren repräsentiert. Es Ergibt sich ein Dreieck mit den Punkten Ursprung, Punkt1 und Punkt2. Mit dem Kreuzprodukt kann der Normalvektor dieses Dreiecks (der Ebene) bestimmt werden. Weiter kannst Du über das Kreuzprodukt zweier Normalvektoren von Ebenen einen 3. Vektor bestimmen, der in beiden Ebenen enthalten ist und den "Schnittpunkt" repräsentiert. In der projektiven Geometrie spricht man hier von einem Dualismus zwischen Punkt und Gerade.

    Die letzte Komponente von schnittp wird nicht mehr 1 sein. Um wieder auf zwei Komponenten zu kommen teilst Du durch die letzte Komponente:

    schnitt_x = schnittp.x() / schnittp.z(); 
      schnitt_y = schnittp.y() / schnittp.z();
    

    wobei man dazu sagen muss, dass, falls die Linien parallel waren, schnittp.z()==0 gilt -- Rundungsfehler mal ignoriert.

    Homogene Koordinaten haben hier auch den Vorteil, dass Linien und Schnittpunkte dargestellt werden können, die im Unendlichen liegen. Ein Schnittpunkt in homogenen Koordinaten (2;3;0) heißt, dass der Schnittpunkt im Unendlichen in der Richtung (2;3) liegt. Mit solchen Fluchtpunkten kannst Du genauso rechnen. Also, einer der 4 Punkte könnte zB so ein Fluchtpunkt sein.

    Übrigens: Es empfiehlt sich mit "normalisierten" Koordinaten zu rechnen. Statt direkt Pixelkoordinaten zu benutzten, sind normalisierte Punktkoordinaten, die im Bereich -1...1 liegen numerisch besser, da sonst stärkere Rundungsfehler bei den Berechnungen auftreten können.

    HTH,
    SP



  • Vielen Dank für diese ausführliche Antwort!

    Habe das für einen schnellen Test mal eben so implementiert:

    int p1[] = { -4,  5, 1 };
    int p2[] = { -2, -5, 1 };
    int p3[] = { -6,  2, 1 };
    int p4[] = {  5,  4, 1 };
    int l1[3], l2[3], s[3];
    double sch[2];
    l1[0] = p1[1] * p2[2] - p1[2] * p2[1];
    l1[1] = p1[2] * p2[0] - p1[0] * p2[2];
    l1[2] = p1[0] * p2[1] - p1[1] * p2[0];
    l2[0] = p3[1] * p4[2] - p3[2] * p4[1];
    l2[1] = p3[2] * p4[0] - p3[0] * p4[2];
    l2[2] = p3[0] * p4[1] - p3[1] * p4[0];
    s[0] = l1[1] * l2[2] - l1[2] * l2[1];
    s[1] = l1[2] * l2[0] - l1[0] * l2[2];
    s[2] = l1[0] * l2[1] - l1[1] * l2[0];
    sch[0] = (double)s[0] / (double)s[2];
    sch[1] = (double)s[1] / (double)s[2];
    

    Und es funktioniert wunderbar. 👍

    Das Problem mit den Rundungsfehlern habe ich nicht, da ich ja nur mit ganzzahligen Koordinaten rechne und keine Division dabei habe, so dass ich einfach s[2] == 0 machen kann um auf Parallelität zu prüfen.

    Eine Frage habe ich dann aber doch noch. Wie meinst du das mit den normalisierten Koordinaten? Muss ich dazu bei jedem Vektor schauen, welcher Teil (x, y, z) der Größte ist und dann x, y und z durch diese Zahl dividieren???
    Am Beispiel von oben:
    int p3[] = { -6, 2, 1 };
    wird zu
    double p3[] = { -6/-6, 2/-6, 1/-6 };
    So in etwa?

    Gruß



  • wchristian schrieb:

    Eine Frage habe ich dann aber doch noch. Wie meinst du das mit den normalisierten Koordinaten? Muss ich dazu bei jedem Vektor schauen, welcher Teil (x, y, z) der Größte ist und dann x, y und z durch diese Zahl dividieren???
    Am Beispiel von oben:
    int p3[] = { -6, 2, 1 };
    wird zu
    double p3[] = { -6/-6, 2/-6, 1/-6 };
    So in etwa?

    Du meinst

    double p3[] = { -6/6.0, 2/6.0, 1/6.0 };
    

    Das kann man so machen (ist auch immer noch korrekt), bringt aber in diesem Fall numerisch eigentlich nichts. Aber es würde dafür sorgen, dass die Längen nicht "explodieren" nach 2 Kreuzprodukten. Die Norm des Kreuzprodukes ist ja immer durch das Produkt der Normen der zwei Eingabevektoren begrenzt, also

    ||cross(a,b)|| = ||a|| * ||b|| * sin(alpha)
    

    wobei alpha der Winkel zwischen a und b ist und ||.|| die Euklidische Norm bezeichnet.

    Ich bezog mich auf das 2D-Koordinatensystem. Das kannst Du so legen, dass (0;0) die linke obere Ecke des Bildschirms ist, und beispielsweise (1023;767) die rechte untere Ecke (Pixelkoordinaten) ist, oder Du verwendest ein anderes Koordinatensystem, wo (-1;-0.75) die linke obere Ecke und (+1;+0.75) die rechte untere Ecke ist (einen 4:3 Bildschirm angenommen) -- oder so ähnlich. Ein solches Skalieren und Verschieben der 2D-Koordinaten würde die Längen und die Richtungen der 3D-Vektoren ändern und die Längen automatisch in der Größenordnung von 1..2 halten. Numerisch ist es von Vorteil, wenn die zwei Vektoren, die "multipliziert" (Kreuzprodukt) werden senkrecht aufeinander stehen -- also, je größer sin(alpha) ist, desto besser. Das kann man zB durch Skalieren und Verschieben beeinflussen.

    So wichtig ist das aber in diesem Fall nicht. Ist wahrscheinlich eher Overkill für Numerik-Freaks.

    Gruß,
    SP



  • @Sebastian: super Beitrag 👍
    So einfach und schön habe ich eine Einführung in 'Homogene Koordinaten' selten gelesen.

    @wchristian: Du tust Dich leichter, wenn Du eine eigene Klasse für einen (math.)Vektor benutzt. Z.B. diesen basic_vector.

    Gruß
    Wenner



  • Das mit der Koordinatentransformation brauche ich, da meine Koordinaten aus Bildpunkten eines Fotos abgeleitet sind. So dass ich in der Praxis auf 4stellige Koordinaten treffe und durch die Multiplikationen kann mir da ganz schnell etwas überlaufen.

    Sind das die nötigen Verfahren dazu?
    http://de.wikipedia.org/wiki/Koordinatentransformation#Verschiebung_.28Translation.29
    http://de.wikipedia.org/wiki/Koordinatentransformation#Skalierung

    Daran werde ich mich jetzt mal versuchen...

    Ergebnis:

    //Die 4 Punkte, p1 und p2 ergeben Linie 1 und p3, p4 ergeben Linie 2
    int p1[] = { -4,  5, 1 };
    int p2[] = { -2, -5, 1 };
    int p3[] = { -6,  2, 1 };
    int p4[] = {  5,  4, 1 };
    //Koordinatentransformation - Verschiebung (Translation) - Bsp-Bild: 1600x1200
    double d1[] = { p1[0] - 800, p1[1] + 600, 1.0 };
    double d2[] = { p2[0] - 800, p2[1] + 600, 1.0 };
    double d3[] = { p3[0] - 800, p3[1] + 600, 1.0 };
    double d4[] = { p4[0] - 800, p4[1] + 600, 1.0 };
    
    //Koordinatentransformation - Skalierung - Bsp-Bild: 1600x1200
    d1[0] /= 1600.0; d1[1] /= 1600;
    d2[0] /= 1600.0; d2[1] /= 1600;
    d3[0] /= 1600.0; d3[1] /= 1600;
    d4[0] /= 1600.0; d4[1] /= 1600;
    
    double l1[3], l2[3], s[3];
    double sch[2];
    l1[0] = d1[1] * d2[2] - d1[2] * d2[1];
    l1[1] = d1[2] * d2[0] - d1[0] * d2[2];
    l1[2] = d1[0] * d2[1] - d1[1] * d2[0];
    l2[0] = d3[1] * d4[2] - d3[2] * d4[1];
    l2[1] = d3[2] * d4[0] - d3[0] * d4[2];
    l2[2] = d3[0] * d4[1] - d3[1] * d4[0];
    s[0] = l1[1] * l2[2] - l1[2] * l2[1];
    s[1] = l1[2] * l2[0] - l1[0] * l2[2];
    s[2] = l1[0] * l2[1] - l1[1] * l2[0];
    sch[0] = s[0] / s[2];
    sch[1] = s[1] / s[2];
    sch[0] *= 1600; sch[1] *= 1600;
    sch[0] += 800; sch[1] -= 600;
    

    Gibt es hier mathematische oder programmiertechnische Probleme? Mit meinen Testwerten rechnet es zumindest richtig 😉
    Wäre es zum Beispiel besser, wenn ich die y-Komponente von d1...d4 durch 1200 teile? Also x-Komponente durch Breite des Bildes und y-Komponente durch dessen Höhe. Ich wollte es jetzt so machen, dass ich mir immer den größten Wert aus Breite und Höhe nehme und durch diesen teile, weil das Seitenverhältnis ja sonst nicht mehr stimmt.
    Da ich am Ende wieder mit diesem Wert(en) multipliziere ( sch[0] *= 1600; sch[1] *= 1600; ) spielt es nach meiner Auffassung für das Ergebnis keine Rolle. Ist das richtig?

    @Werner Salomon
    Deine Klasse kann ich leider nicht benutzen, da ich dazu verdammt bin mit Visual C++ 6.0 zu arbeiten.



  • wchristian schrieb:

    Das mit der Koordinatentransformation brauche ich, da meine Koordinaten aus Bildpunkten eines Fotos abgeleitet sind. So dass ich in der Praxis auf 4stellige Koordinaten treffe und durch die Multiplikationen kann mir da ganz schnell etwas überlaufen.

    Nimm double, dann solltest Du keine Probleme haben. Eine Transformation auf den Bereich [-1,1] ist in Deinem Fall nicht nötig.

    wchristian schrieb:

    @Werner Salomon
    Deine Klasse kann ich leider nicht benutzen, da ich dazu verdammt bin mit Visual C++ 6.0 zu arbeiten.

    .. kannst Du schon benutzen. Ändere sie halt, bis es geht. Du kannst auch auf die boost.operators verzichten, wenn Du die fehlenden Methoden selber implementierst. Wenn Du da Schwierigkeiten hast, frag' noch mal nach.

    Es wird dann nämlich ziemlich einfach:

    #include <iostream>
    #include <boost/rational.hpp>
    #include "Vektor.h" //basic_vector<>
    // Bem.: basic_vector inklusive:   friend T Z( const basic_vector& v ) { return v.m_z; }
    
    int main()
    {
        using namespace std;
        typedef basic_vector< double > Vec;
        //Die 4 Punkte, p1 und p2 ergeben Linie 1 und p3, p4 ergeben Linie 2
        Vec p1( -4,  5, 1 );
        Vec p2( -2, -5, 1 );
        Vec p3( -6,  2, 1 );
        Vec p4(  5,  4, 1 );
    
        // linie1   = cross( punkt1, punkt2 );  
        // linie2   = cross( punkt3, punkt4 );  
        // schnittp = cross( linie1, linie2 );  lt. Posting von Sebastian Pizer
        Vec schnittpkt = (p1 % p2) % (p3 % p4);
    
        schnittpkt /= Z( schnittpkt ); // normalisieren
        cout << "Ergebnis: " << schnittpkt << endl;
    
        return 0;
    }
    

    Wenn Du die Zeile 9 durch

    typedef basic_vector< boost::rational< int > > Vec;
    

    ersetzt und das notwendige boost.rational include hinzufügst, funktioniert es genauso. Die Ausgabe wäre dann:

    Ergebnis: -199/57 140/57 1/1
    

    also identisch mit Deinem Ergebnis.

    Gruß
    Werner



  • Werner Salomon schrieb:

    @Sebastian: super Beitrag 👍

    Danke! 🙂

    wchristian schrieb:

    Ist das richtig?

    Ja.

    Tu Dir einen Gefallen und benutze eine Klasse für 3D-Vektoren und eine Funktion für das Kreuzprodukt, statt "manuell zu inlinen". Damit erhöst Du die Lesbarkeit und veringerst die Fehleranfälligkeit. Eine einfache Version von "vec3d & co" kostet keine 50 Zeilen:

    class vec3d
    {
      double xyz[3];
    public:
      vec3d() { for (int k=0; k<3; ++k) xyz[k]=0; }
      vec3d(double x, double y, double z)
      { xyz[0]=x; xyz[1]=y; xyz[2]=z; }
    
      double      & operator[](unsigned idx)
      { assert(idx<3); return xyz[idx]; }
      double const& operator[](unsigned idx) const
      { assert(idx<3); return xyz[idx]; }
    
      vec3d& operator+=(vec3d const& v)
      { for (int k=0; k<3; ++k) xyz[k]+=v.xyz[k]; return *this; }
      vec3d& operator-=(vec3d const& v)
      { for (int k=0; k<3; ++k) xyz[k]-=v.xyz[k]; return *this; }
      vec3d& operator*=(double s)
      { for (int k=0; k<3; ++k) xyz[k]*=s; return *this; }
      vec3d& operator/=(double s)
      { for (int k=0; k<3; ++k) xyz[k]/=s; return *this; }
    };
    
    inline double dot(vec3d const& a, vec3d const& b)
    { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
    
    inline vec3d cross(vec3d const& a, vec3d const& b)
    {
      return vec3d(
        a[1]*b[2]-a[2]*b[1],
        a[2]*b[0]-a[0]*b[2],
        a[0]*b[1]-a[1]*b[0]
      );
    }
    
    inline vec3d operator+(vec3d const& a, vec3d const& b)
    { vec3d ret = a; ret += b; return ret; }
    inline vec3d operator-(vec3d const& a, vec3d const& b)
    { vec3d ret = a; ret -= b; return ret; }
    inline vec3d operator*(vec3d const& a, double s)
    { vec3d ret = a; ret *= s; return ret; }
    inline vec3d operator*(double s, vec3d const& a)
    { vec3d ret = a; ret *= s; return ret; }
    inline vec3d operator/(vec3d const& a, double s)
    { vec3d ret = a; ret /= s; return ret; }
    inline double operator/(vec3d const& a, vec3d const& b)
    { return dot(a,b)/dot(b,b); } // least squares fit
    

    (nicht ausgiebig getestet)

    Das % für ein Kreuzprodukt zu benutzen, finde ich nicht so schön.

    Gruß,
    SP



  • Sebastian Pizer schrieb:

    inline vec3d operator+(vec3d const& a, vec3d const& b)
    { vec3d ret = a; ret += b; return ret; }
    

    Verbesserungsvorschlag:

    inline vec3d operator+(vec3d a, vec3d const& b)
    { return a += b; }
    

    siehe dazu auch den ersten Teil der boost.operators.Symmetry Note

    Sebastian Pizer schrieb:

    inline double operator/(vec3d const& a, vec3d const& b)
    { return dot(a,b)/dot(b,b); } // least squares fit
    

    Interessant! wofür braucht man das? und was hat das mit 'least squares fit' zu tun?

    Gruß
    Werner



  • Werner Salomon schrieb:

    Sebastian Pizer schrieb:

    inline vec3d operator+(vec3d const& a, vec3d const& b)
    { vec3d ret = a; ret += b; return ret; }
    

    Verbesserungsvorschlag:

    inline vec3d operator+(vec3d a, vec3d const& b)
    { return a += b; }
    

    Hier ist eine Tabelle, die zeigt, wie viele Kopien beide Versionen in verschiedenen Situationen erzeugen. Die erste Zahl gehört zu meiner Version, die zweite zu Deiner. Die vier Fälle, die ich betrachte ergeben sich aus den Kombinationen von "Compiler kann/kann nicht NRVO" und "das erste Argument ist/ist kein ein Rvalue":

    Anzahl der | Rvalue | Rvalue
      Kopien   |   ja   |  nein
    -----------+--------+-------
    NRVO aus   |  2 / 1 |  2 / 2
    NRVO an    |  1 / 1 |  1 / 2
    

    Da sowohl MSVC (aktuelle Version, release mode) als auch der GCC die "named return value optimization" beherrschen, sehe ich nicht ein, warum ich die zweite Version nutzen soll. Es macht wahrscheinlich eh keinen großen Unterschied bei einem Typen wie vec3d, welcher "trivial kopierbar" ist.

    Werner Salomon schrieb:

    Sebastian Pizer schrieb:

    inline double operator/(vec3d const& a, vec3d const& b)
    { return dot(a,b)/dot(b,b); } // least squares fit
    

    Interessant! wofür braucht man das? und was hat das mit 'least squares fit' zu tun?

    Das ist einfach das Gegenstück zum Skalieren eines Vektors:
    vector_y = scalar_s * vector_x;
    scalar_s = vector_y / vector_x;

    Das letzte ist allerdings ein 3fach überbestimmtes Gleichungssystem, welches eine Lösung im Sinne der kleinsten Fehlerquadrate besitzt:

    min  || s * x - y ||
     s
    

    Man kann diesen Operator beispielsweise dazu benutzen, Vektoren auf andere zu projizieren.

    Gruß,
    SP


  • Mod

    Sebastian Pizer schrieb:

    Werner Salomon schrieb:

    Sebastian Pizer schrieb:

    inline vec3d operator+(vec3d const& a, vec3d const& b)
    { vec3d ret = a; ret += b; return ret; }
    

    Verbesserungsvorschlag:

    inline vec3d operator+(vec3d a, vec3d const& b)
    { return a += b; }
    

    Hier ist eine Tabelle, die zeigt, wie viele Kopien beide Versionen in verschiedenen Situationen erzeugen. Die erste Zahl gehört zu meiner Version, die zweite zu Deiner. Die vier Fälle, die ich betrachte ergeben sich aus den Kombinationen von "Compiler kann/kann nicht NRVO" und "das erste Argument ist/ist kein ein Rvalue":

    Variante 2 sollte so aussehen

    inline vec3d operator+(vec3d a, vec3d const& b)
    { a += b; return a; }
    

    Sonst ist NRVO nicht möglich. Kein existierender Compiler führt allerdings NRVO mit Funktionsparamtern durch, es sei denn, die Funktion wird inline substituiert, in welchem Falle überflüssige Kopien wahrscheinlich auch ohne NRVO per as-if eliminiert würden.

    Vorteilhaft ist diese Variante dann, wenn wir Konvertierungen betrachten, in diesem Falle spart Variante 2 theoretisch eine unnötige Kopie. Allerdings besitzt der Vektor, der hier betrachtet wird, keine Konvertierungskonstruktoren...

    Zudem darf die Weisheit, + durch += zu implementieren, in vielen Fällen bezweifelt werden, wenn Geschwindigkeit eine Rolle spielt. Im Allgemeinen ist das nur dann ohne Effizienzverlust möglich, wenn die Objekte wie hier vergleichsweise klein sind und ohne dynamische Allokierung auskommen.


Anmelden zum Antworten