Wie überladen Sie std :: swap ()

  • std::swap() wird von vielen Standardcontainern (wie std::list und std::vector) während der Sortierung und sogar der Zuweisung verwendet.

    Aber die Die std-Implementierung von swap() ist für benutzerdefinierte Typen sehr verallgemeinert und eher ineffizient.

    Durch Überladen von std::swap() mit einer benutzerdefinierten typspezifischen Implementierung kann Effizienz erzielt werden. Aber wie können Sie es implementieren, damit es von den std-Containern verwendet wird?

    22 March 2013
    sbi
4 answers
  • Der richtige Weg zum Überladen von Swap ist, dass Sie ihn in den gleichen Namespace schreiben, wie der Swap, den Sie austauschen, damit er über eine argumentsabhängige Suche (ADL) gefunden werden kann. Eine besonders einfache Sache ist:

     class X
    {
        // ...
        friend void swap(X& a, X& b)
        {
            using std::swap; // bring in swap for built-in types
    
            swap(a.base1, b.base1);
            swap(a.base2, b.base2);
            // ...
            swap(a.member1, b.member1);
            swap(a.member2, b.member2);
            // ...
        }
    };
     
    21 April 2010
    Dave Abrahams
  • Achtung Mozza314

    Hier ist eine Simulation der Auswirkungen eines generischen std::algorithm, der std::swap aufruft und den Benutzer hat Tauschen Sie den Namespace std aus. Da dies ein Experiment ist, verwendet diese Simulation namespace exp anstelle von namespace std.

     // simulate <algorithm>
    
    #include <cstdio>
    
    namespace exp
    {
    
        template <class T>
        void
        swap(T& x, T& y)
        {
            printf("generic exp::swap\n");
            T tmp = x;
            x = y;
            y = tmp;
        }
    
        template <class T>
        void algorithm(T* begin, T* end)
        {
            if (end-begin >= 2)
                exp::swap(begin[0], begin[1]);
        }
    
    }
    
    // simulate user code which includes <algorithm>
    
    struct A
    {
    };
    
    namespace exp
    {
        void swap(A&, A&)
        {
            printf("exp::swap(A, A)\n");
        }
    
    }
    
    // exercise simulation
    
    int main()
    {
        A a[2];
        exp::algorithm(a, a+2);
    }
     

    Für mich das Gibt Folgendes aus:

     generic exp::swap
     

    Wenn Ihr Compiler etwas anderes ausgibt, wird die "Zwei-Phasen-Suche" nicht korrekt implementiert "für Vorlagen.

    Wenn Ihr Compiler konform ist (zu einem beliebigen C ++ 98/03/11), wird dieselbe Ausgabe angezeigt, die ich anzeigt. Und in diesem Fall passiert genau das, was Sie fürchten, passiert. Und Ihr swap in den Namensraum std (exp) zu stellen, hat dies nicht verhindert.

    Dave und ich sind beide Komiteemitglieder und haben diesen Bereich bearbeitet der Standard für ein Jahrzehnt (und nicht immer in Übereinstimmung miteinander). Dieses Problem ist jedoch schon lange gelöst, und wir sind uns beide einig, wie es gelöst wurde. Ignorieren Sie Daves Expertenmeinung / -antwort in diesem Bereich auf eigene Gefahr.

    Diese Ausgabe erschien nach Veröffentlichung von C ++ 98. Ab ca. 2001 begannen Dave und ich, diesen Bereich zu bearbeiten . Und das ist die moderne Lösung:

     // simulate <algorithm>
    
    #include <cstdio>
    
    namespace exp
    {
    
        template <class T>
        void
        swap(T& x, T& y)
        {
            printf("generic exp::swap\n");
            T tmp = x;
            x = y;
            y = tmp;
        }
    
        template <class T>
        void algorithm(T* begin, T* end)
        {
            if (end-begin >= 2)
                swap(begin[0], begin[1]);
        }
    
    }
    
    // simulate user code which includes <algorithm>
    
    struct A
    {
    };
    
    void swap(A&, A&)
    {
        printf("swap(A, A)\n");
    }
    
    // exercise simulation
    
    int main()
    {
        A a[2];
        exp::algorithm(a, a+2);
    }
     

    Die Ausgabe ist:

     swap(A, A)
     

    Aktualisieren

    Folgende Beobachtung wurde gemacht:

     namespace exp
    {    
        template <>
        void swap(A&, A&)
        {
            printf("exp::swap(A, A)\n");
        }
    
    }
     

    funktioniert! Warum also nicht das verwenden?

    Betrachten Sie den Fall, dass Ihre A eine Klassenvorlage ist:

     // simulate user code which includes <algorithm>
    
    template <class T>
    struct A
    {
    };
    
    namespace exp
    {
    
        template <class T>
        void swap(A<T>&, A<T>&)
        {
            printf("exp::swap(A, A)\n");
        }
    
    }
    
    // exercise simulation
    
    int main()
    {
        A<int> a[2];
        exp::algorithm(a, a+2);
    }
     

    Jetzt funktioniert es nicht mehr. : - (

    Sie könnten also swap in den Namespace std setzen und funktionieren lassen. Aber Sie müssen daran denken, swap in den Namespace von A zu setzen für den Fall, dass Sie eine Vorlage haben: A<T>. Und da beide Fälle funktionieren, wenn Sie swap in den Namespace von A einfügen, ist es einfacher zu merken (und an

    31 August 2015
    Andrea Corbellini
  • Sie dürfen (nach dem C ++ - Standard) nicht überladen, std :: swap zu überlasten. Sie können jedoch dem std-Namespace speziell Schablonenspezialisierungen für Ihre eigenen Typen hinzufügen. Wenn Sie beispielsweise

     namespace std
    {
        template<>
        void swap(my_type& lhs, my_type& rhs)
        {
           // ... blah
        }
    }
     

    wählen, werden die Verwendungen in den std-Containern (und anderswo) Ihre Spezialisierung anstelle der allgemeinen auswählen one.

    Beachten Sie auch, dass die Bereitstellung einer Basisklassenimplementierung von Swap für Ihre abgeleiteten Typen nicht gut genug ist. Z.B. Wenn Sie

     class Base
    {
        // ... stuff ...
    }
    class Derived : public Base
    {
        // ... stuff ...
    }
    
    namespace std
    {
        template<>
        void swap(Base& lha, Base& rhs)
        {
           // ...
        }
    }
     

    haben, funktioniert dies für Basisklassen, aber wenn Sie versuchen, zwei abgeleitete Objekte auszutauschen, ist dies der Fall Verwenden Sie die generische Version von std, da der templatierte Swap eine exakte Übereinstimmung ist (und es das Problem vermeidet, nur die 'Basis'-Teile Ihrer abgeleiteten Objekte zu vertauschen).

    HINWEIS: I Ich habe dies aktualisiert, um die falschen Bits aus meiner letzten Antwort zu entfernen. D'oh! (Danke puetzk und j_random_hacker für den Hinweis)

    27 January 2014
    Dan Nissenbaum
  • Obwohl es richtig ist, dass im Allgemeinen keine Elemente zum std ::-Namespace hinzugefügt werden sollen, ist das Hinzufügen von Schablonenspezialisierungen für benutzerdefinierte Typen ausdrücklich zulässig. Überladen der Funktionen nicht. Dies ist ein geringfügiger Unterschied: -)

    17.4.3.1/1 Es ist nicht definiert, dass ein C ++ - Programm Deklarationen oder Definitionen hinzufügt auf Namespace std oder Namespaces mit Namespace std, sofern nicht anderweitig angegeben. Ein Programm kann dem Namespace std Schablonenspezialisierungen für eine beliebige Standardbibliothekvorlage hinzufügen. Eine solche Spezialisierung (vollständig oder teilweise) einer Standardbibliothek führt zu undefiniertem Verhalten, es sei denn, die Deklaration hängt von einem benutzerdefinierten Namen der externen Verknüpfung ab, und die Vorlagen-Spezialisierung entspricht der Standardbibliothek Voraussetzungen für die Originalvorlage.

    Eine Spezialisierung von std :: swap würde folgendermaßen aussehen:

     namespace std
    {
        template<>
        void swap(myspace::mytype& a, myspace::mytype& b) { ... }
    }
     

    Ohne Vorlage & lt; & gt; Es wäre jedoch eine Überladung, die undefiniert ist, und keine Spezialisierung, die zulässig ist. @ Wilkas Vorschlag, den Standard-Namespace zu ändern, funktioniert möglicherweise mit Benutzercode (aufgrund der Koenig-Suche, die die Version ohne Namespace bevorzugt). Dies ist jedoch nicht garantiert, und es ist eigentlich nicht beabsichtigt (die STL-Implementierung sollte die vollständige Implementierung verwenden.) -qualified std :: swap).

    Es gibt eine Thread auf comp.lang.c ++. moderiert mit einer langen -Diskussion des Themas. Bei dem Großteil handelt es sich jedoch um eine teilweise Spezialisierung (was derzeit nicht sinnvoll ist).

    21 September 2008
    puetzk