come fornire una funzione di scambio per la mia classe?


89

Qual è il modo corretto per abilitare i miei swapalgoritmi in STL?

1) Membro swap. Non std::swaputilizzare SFINAE trucco per utilizzare il membro swap.

2) In piedi libero swapnello stesso spazio dei nomi.

3) Parziale specializzazione di std::swap.

4) Tutto quanto sopra.

Grazie.

EDIT: Sembra che non abbia espresso chiaramente la mia domanda. Fondamentalmente, ho una classe modello e ho bisogno di algoritmi STL per utilizzare il metodo di scambio (efficiente) che ho scritto per quella classe.

Risposte:


95
  1. è l' uso corretto di swap. Scrivilo in questo modo quando scrivi codice "libreria" e desideri abilitare ADL (ricerca dipendente dall'argomento) swap. Inoltre, questo non ha nulla a che fare con SFINAE.
// 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. È il modo corretto per fornire una swapfunzione per la tua classe.
namespace Foo {

class Bar{}; // dummy

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

}

Se swapora viene utilizzato come mostrato in 1), la funzione verrà trovata. Inoltre, puoi rendere quella funzione un amico se ne hai assolutamente bisogno, o fornire un membro swapchiamato dalla funzione gratuita:

// 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. Intendi una specializzazione esplicita. Il parziale è ancora qualcos'altro e non è possibile per le funzioni, solo per le strutture / classi. In quanto tale, dal momento che non si può specializzare std::swapper le classi template, è necessario fornire una funzione libera nello spazio dei nomi. Non male, se così posso dire. Ora, è anche possibile una specializzazione esplicita, ma generalmente non vuoi specializzare un modello di funzione :
namespace std
{  // only allowed to extend namespace std with specializations

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

}
  1. No, poiché 1) è distinto da 2) e 3). Inoltre, avere sia 2) che 3) porterà ad avere sempre 2) selezionati, perché si adatta meglio.

8
Il tuo (1) e quello della domanda (1) non sono allineati, a meno che non stia interpretando male qualcosa. Eppure, +1
Dennis Zickefoose

1
@Xeo. Grazie per il tuo contributo. Ho modificato la mia domanda. STL usa lo swap come lo hai descritto nel caso 1?
pic11

1
@pic: Sì, l'STL utilizzerà lo scambio ADL che ho mostrato in 1), ma solo se è presente come funzione gratuita, non solo come funzione membro. Vedi 2) e 3), entrambe le versioni saranno scelte dagli algoritmi. Consiglierei 2), poiché 3) è obsoleto e considerato una cattiva pratica.
Xeo

2
Il commento nella prima parte di codice è fuorviante. using std::swap;non abilita ADL, consente solo al compilatore di individuare std::swapse ADL non trova un corretto sovraccarico.
David Rodríguez - dribeas

6
Questa risposta è tecnicamente corretta, ma ha assolutamente bisogno di modifiche per chiarezza. L'OP (1) non è la risposta corretta come una lettura troppo rapida in questa risposta sembrerebbe indicare erroneamente.
Howard Hinnant

1

Per rispondere all'EDIT, dove le classi possono essere classi modello, non hai affatto bisogno di specializzazione. considera una classe come questa:

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

puoi definire classi come:

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

se vuoi essere in grado di creare una funzione per implementare tutti e 3 gli swap (non che questa classe di esempio lo garantisca) fai proprio come Xeo ha detto in (2) ... senza specializzazione ma crea solo una normale funzione template:

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);
}

La funzione del modello di scambio dovrebbe trovarsi nello stesso spazio dei nomi della classe che stai tentando di scambiare. il seguente metodo troverà e utilizzerà quello scambio anche se non stai facendo riferimento a quello spazio dei nomi usando ADL:

using std::swap;
swap(a,b);

0

Sembra che (2) ( free standing swapnello stesso spazio dei nomi in cui è dichiarata la classe definita dall'utente ) sia l'unico modo consentito per fornire swapuna classe definita dall'utente, perché l'aggiunta di dichiarazioni allo spazio dei nomi stdè generalmente un comportamento indefinito. Estensione dello spazio dei nomi std (cppreference.com) :

È un comportamento indefinito aggiungere dichiarazioni o definizioni allo spazio dei nomi stdo a qualsiasi spazio dei nomi annidato all'interno std, con alcune eccezioni indicate di seguito

E swapnon è indicato come una di quelle eccezioni. Quindi l'aggiunta del proprio swapoverload allo stdspazio dei nomi è un comportamento indefinito.

Si dice anche che la libreria standard utilizzi una chiamata non qualificata alla swapfunzione per chiamare definita swapdall'utente per una classe utente se swapviene fornita tale definita dall'utente .

Scambiabile (cppreference.com) :

Molte funzioni della libreria standard (ad esempio, molti algoritmi) si aspettano che i loro argomenti soddisfino Swappable , il che significa che ogni volta che la libreria standard esegue uno scambio, utilizza l'equivalente di using std::swap; swap(t, u);.

swap (www.cplusplus.com) :

Molti componenti della libreria standard (all'interno std) chiamano swapin modo non qualificato per consentire la chiamata di overload personalizzati per tipi non fondamentali invece di questa versione generica: vengono selezionati gli overload personalizzati swapdichiarati nello stesso spazio dei nomi del tipo per cui sono forniti attraverso la ricerca dipendente dall'argomento su questa versione generica.

Ma si noti che l'utilizzo diretto della std::swapfunzione per una classe definita dall'utente chiama la versione generica di std::swapinvece di quella definita dall'utente swap:

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

Quindi si consiglia di chiamare la swapfunzione nel codice utente nello stesso modo in cui si fa nella libreria standard:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.