Frage

std::swap() wird von vielen std Behälter (wie z.B. std::list und std::vector) während der Sortierung und sogar Abtretung an.

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

Somit kann die Effizienz gewonnen werden durch überlastung std::swap() mit einem benutzerdefinierten Typ-spezifischen Implementierung.Aber wie können Sie es implementieren, so dass es verwendet werden durch die std-Containern?

War es hilfreich?

Lösung

Der richtige Weg, um eine überlastung swap ist, schreiben Sie es in den gleichen namespace wie das, was du bist-swapping, so dass es sein kann, gefunden über argument-dependent lookup (ADL).Eine besonders einfache Sache zu tun 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);
        // ...
    }
};

Andere Tipps

Aufmerksamkeit Mozza314

Hier ist eine simulation der Auswirkungen einer generischen std::algorithm aufrufen std::swap, und mit der Benutzer Ihre swap-in-namespace std.Als dies ist ein experiment, diese simulation verwendet namespace exp statt 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 ist dies druckt:

generic exp::swap

Wenn Ihr compiler gibt, etwas anderes dann ist es nicht richtig, der "zwei-Phasen-lookup" für Vorlagen.

Wenn Ihr compiler ist konform (auf jeder der C++98/03/11), dann wird es geben Sie die gleiche Ausgabe, die ich zeigen.Und in diesem Fall genau das, was Sie fürchten, wird passieren, nicht passieren.Und die Umsetzung Ihrer swap in namespace std (exp) nicht verhindern, dass es passiert.

Dave und ich sind beide Mitglieder des Ausschusses und haben gearbeitet in diesem Bereich der standard für ein Jahrzehnt (und nicht immer in Abstimmung mit jeder andere).Aber dieses Problem hat sich für eine lange Zeit, und wir beide Stimmen überein, wie es bezahlt wurde.Ignorieren Dave Experten-Meinung/Antwort in diesem Bereich auf eigene Gefahr.

Dieses Problem kam ans Licht, nachdem C++98 veröffentlicht wurde.Ab etwa 2001 Dave und ich begann zu arbeiten in diesem Bereich.Und dies 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);
}

Ausgabe:

swap(A, A)

Update

Eine Beobachtung gemacht worden, dass:

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

}

funktioniert!Warum also nicht nutzen?

Betrachten wir den Fall, dass Sie Ihre A ist eine Klasse Vorlage:

// 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 wieder.:-(

So könnte man swap im namespace std, und habe es funktioniert.Aber Sie müssen nicht vergessen, swap in A's namespace, für den Fall, wenn Sie eine Vorlage: A<T>.Und da beide Fälle werden Sie arbeiten, wenn Sie legen swap in A's namespace, es ist einfach leichter zu merken (und zu lehren), es einfach zu tun, dass ein Weg.

Sie sind nicht erlaubt (der C++ - standard), um eine überlastung std::swap, Sie sind jedoch ausdrücklich gestattet add template Spezialisierungen für Ihre eigenen Arten zu den std-namespace.E. g.

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

dann den Gepflogenheiten in der std-Container (und überall sonst) wird, wählen Sie Ihre Spezialisierung statt der Allgemeinen einer.

Beachten Sie auch, dass die Basisklasse die Implementierung von swap ist nicht gut genug für die abgeleiteten Typen.E. g.wenn Sie haben

class Base
{
    // ... stuff ...
}
class Derived : public Base
{
    // ... stuff ...
}

namespace std
{
    template<>
    void swap(Base& lha, Base& rhs)
    {
       // ...
    }
}

dies funktioniert für die Basis-Klassen, aber wenn Sie versuchen, swap zwei Abgeleitete Objekte werden die generischen version von std, weil die Vorlagen-swap ist eine exakte übereinstimmung (und es vermeidet das problem der nur durch das vertauschen der 'base' Teile von Ihr abgeleitete Objekte).

HINWEIS:Ich habe aktualisiert, um die falschen bits aus meiner letzten Antwort.D ' Oh!(Dank puetzk und j_random_hacker für den Hinweis it out)

Es ist zwar richtig, dass man nicht generell Dinge hinzufügen, um den std::namespace hinzufügen von template-Spezialisierungen für benutzerdefinierte Typen ist ausdrücklich erlaubt.Überladen von Funktionen nicht.Dies ist eine feine Unterschied :-)

17.4.3.1/1 Es ist nicht definiert, für die ein C++ - Programm zum hinzufügen von Erklärungen oder Definitionen namespace std oder namespaces mit namespace std-sofern nicht anders angegeben.Ein Programm hinzufügen können-template-Spezialisierungen für jede standard library template-namespace std.Eine solche Spezialisierung (vollständige oder teilweise) eines standard-Bibliothek results in undefined Verhalten, es sei denn, die Erklärung ist, hängt vom Benutzer definierte name externe Bindung und es sei denn, der template-Spezialisierung erfüllt die standard library Anforderungen für die original-Vorlage.

Eine Spezialisierung der std::swap würde wie folgt Aussehen:

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

Ohne die Vorlage<> bisschen, es wäre eine überlastung, die nicht definiert ist, sondern als eine Spezialisierung, die ist gestattet.@Wilka ist, empfehlen Ansatz der änderung der Standard-namespace kann das arbeiten mit Benutzer-code (aufgrund Koenig-lookup lieber die namespace-weniger-version), aber es ist nicht garantiert, und in der Tat ist nicht wirklich soll (die STL-Implementierung sollte, verwenden Sie den vollqualifizierten std::swap).

Es ist ein thread auf comp.lang.c++.moderiert mit einem lange Koreferat zu dem Thema.Die meisten von es ist über partielle Spezialisierung, obwohl (die gibt es zurzeit keine gute Möglichkeit, das zu tun).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top