Wie stelle ich eine Swap-Funktion für meine Klasse bereit?

85

Was ist der richtige Weg, um meine swapin STL-Algorithmen zu aktivieren ?

1) Mitglied swap. Does std::swapverwendete SFINAE Trick , um das Element zu verwenden swap.

2) Freistehend swapim gleichen Namespace.

3) Teilspezialisierung von std::swap.

4) Alle oben genannten.

Danke dir.

EDIT: Sieht so aus, als hätte ich meine Frage nicht klar formuliert. Grundsätzlich habe ich eine Vorlagenklasse und ich benötige STL-Algen, um die (effiziente) Swap-Methode zu verwenden, die ich für diese Klasse geschrieben habe.

pic11
quelle

Antworten:

93
  1. ist die richtige Verwendung von swap. Schreiben Sie es so, wenn Sie "Bibliotheks" -Code schreiben und ADL (argumentabhängige Suche) aktivieren möchten swap. Auch das hat nichts mit SFINAE zu tun.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. Ist der richtige Weg, um eine swapFunktion für Ihre Klasse bereitzustellen .
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

Wenn swapes jetzt wie in 1) gezeigt verwendet wird, wird Ihre Funktion gefunden. Sie können diese Funktion auch zu einem Freund machen, wenn Sie dies unbedingt benötigen, oder ein Mitglied bereitstellen swap, das von der freien Funktion aufgerufen wird:

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. Sie meinen eine explizite Spezialisierung. Partial ist noch etwas anderes und auch für Funktionen nicht möglich, nur Strukturen / Klassen. Als solches , da Sie nicht spezialisieren können std::swapfür Template - Klassen, Sie haben eine freie Funktion in Ihrem Namensraum zur Verfügung stellen zu. Keine schlechte Sache, wenn ich so sagen darf. Jetzt ist auch eine explizite Spezialisierung möglich, aber im Allgemeinen möchten Sie eine Funktionsvorlage nicht spezialisieren :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Nein, da 1) sich von 2) und 3) unterscheidet. Wenn Sie sowohl 2) als auch 3) haben, wird immer 2) ausgewählt, da es besser passt.
Xeo
quelle
8
Ihre (1) und die (1) der Frage stimmen nicht wirklich überein, es sei denn, ich verstehe etwas falsch. Trotzdem +1
Dennis Zickefoose
1
@ Xeo. Vielen Dank für Ihre Eingabe. Ich habe meine Frage bearbeitet. Verwendet STL Swap, wie Sie es in Fall 1 beschrieben haben?
Bild
1
@pic: Ja, die STL verwendet den in 1) gezeigten ADL-Swap, aber nur, wenn er als freie Funktion vorhanden ist, nicht nur als Mitgliedsfunktion. Siehe 2) und 3), beide Versionen werden von Algorithmen ausgewählt. Ich würde 2) raten, da 3) veraltet ist und als schlechte Praxis angesehen wird.
Xeo
2
Der Kommentar im ersten Code ist irreführend. using std::swap;aktiviert ADL nicht, sondern ermöglicht dem Compiler nur zu ermitteln, std::swapob ADL keine ordnungsgemäße Überladung findet.
David Rodríguez - Dribeas
6
Diese Antwort ist technisch korrekt, muss jedoch aus Gründen der Klarheit dringend bearbeitet werden. Die OPs (1) sind nicht die richtige Antwort, da eine zu schnelle Ablesung in dieser Antwort fälschlicherweise darauf hindeutet.
Howard Hinnant
1

Um die EDIT zu beantworten, bei der es sich bei den Klassen möglicherweise um Vorlagenklassen handelt, benötigen Sie überhaupt keine Spezialisierung. Betrachten Sie eine Klasse wie diese:

template <class T>
struct vec3
{
    T x,y,z;
};

Sie können Klassen definieren wie:

vec3<float> a;
vec3<double> b;
vec3<int> c;

Wenn Sie in der Lage sein möchten, eine Funktion zum Implementieren aller drei Swaps zu erstellen (nicht, dass diese Beispielklasse dies rechtfertigt), tun Sie genau wie in (2) Xeo gesagt ... ohne Spezialisierung, sondern erstellen einfach eine reguläre Vorlagenfunktion:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

Die Swap-Vorlagenfunktion sollte sich im selben Namespace befinden wie die Klasse, die Sie austauschen möchten. Die folgende Methode findet und verwendet diesen Swap, obwohl Sie diesen Namespace nicht mit ADL referenzieren:

using std::swap;
swap(a,b);
Ben
quelle
0

Es scheint, dass (2) ( freistehend swapim selben Namespace, in dem die benutzerdefinierte Klasse deklariert ist ) die einzige zulässige Möglichkeit ist, swapeine benutzerdefinierte Klasse bereitzustellen , da das Hinzufügen von Deklarationen zum Namespace stdim Allgemeinen ein undefiniertes Verhalten ist. Erweitern des Namespace std (cppreference.com) :

Es ist ein undefiniertes Verhalten, Deklarationen oder Definitionen zum Namespace stdoder zu einem darin verschachtelten Namespace hinzuzufügen std, mit einigen unten aufgeführten Ausnahmen

Und swapwird nicht als eine dieser Ausnahmen bezeichnet. Das Hinzufügen einer eigenen swapÜberladung zum stdNamespace ist daher ein undefiniertes Verhalten.

Es wird auch gesagt, dass die Standardbibliothek einen unqualifizierten Aufruf der swapFunktion verwendet, um benutzerdefinierte Aufrufe swapfür eine Benutzerklasse aufzurufen, wenn diese benutzerdefiniert swapbereitgestellt werden.

Austauschbar (cppreference.com) :

Viele Standardbibliotheksfunktionen (z. B. viele Algorithmen) erwarten, dass ihre Argumente Swappable erfüllen. Dies bedeutet, dass die Standardbibliothek jedes Mal, wenn sie einen Swap durchführt, das Äquivalent von verwendet using std::swap; swap(t, u);.

Swap (www.cplusplus.com) :

Viele Komponenten der Standardbibliothek (innerhalb std) -Aufruf swapin einer unqualifizierten Weise individuelle Überlastungen für Nicht-Grundtypen zu ermöglichen , anstelle dieser generischen Version aufgerufen werden: Benutzerdefinierte Überlastungen swaperklärte im gleichen Namensraum wie die Art , für die sie vorgesehen sind , erhalten ausgewählt durch argumentabhängige Suche über diese generische Version.

Beachten Sie jedoch, dass die direkte Verwendung der std::swapFunktion für eine benutzerdefinierte Klasse die generische Version von std::swapanstelle der benutzerdefinierten aufruft swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

Es wird daher empfohlen, die swapFunktion im Benutzercode auf dieselbe Weise aufzurufen wie in der Standardbibliothek:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
anton_rh
quelle