40

Un libro di testo Ho notato che è possibile fornire la propria implementazione per le funzioni di libreria standard come swap(x,y) tramite la specializzazione del modello per l'overloading delle funzioni. Questo sarebbe utile per tutti i tipi che possono beneficiare di qualcosa di diverso da uno scambio di incarichi, come ad esempio STL containers (che già hanno scritto swap, lo so).Template Specialization VS Function Overloading

Le mie domande sono:

  1. Cosa c'è di meglio: modello di specializzazione per dare l'implementazione specializzati di swap, o una funzione sovraccarico fornendo le esatte parametri che si desidera utilizzare, senza un modello?

  2. Perché è meglio? O se sono uguali, perché è questo?

risposta

60

Breve storia: sovraccarico quando è possibile, specializzarsi quando è necessario.

Lunga storia: C++ tratta specializzazioni e sovraccarichi in modo molto diverso. Questo è meglio spiegato con un esempio.

template <typename T> void foo(T); 
template <typename T> void foo(T*); // overload of foo(T) 
template <>   void foo<int>(int*); // specialisation of foo(T*) 

foo(new int); // calls foo<int>(int*); 

Ora scambiamo gli ultimi due.

template <typename T> void foo(T); 
template <>   void foo<int*>(int*); // specialisation of foo(T) 
template <typename T> void foo(T*); // overload of foo(T) 

foo(new int); // calls foo(T*) !!! 

Il compilatore esegue la risoluzione di sovraccarico prima ancora di esaminare le specializzazioni. Quindi, in entrambi i casi, la risoluzione di sovraccarico sceglie foo(T*). Tuttavia, solo nel primo caso trova foo<int*>(int*) perché nel secondo caso la specializzazione int* è una specializzazione di foo(T), non foo(T*).


Hai citato std::swap. Questo rende le cose ancora più complicate.

Lo standard dice che è possibile aggiungere specializzazioni allo spazio dei nomi std. Ottimo, quindi hai un po 'di tipo e ha uno scambio performante quindi ti specializzi solo nello spazio dei nomi swap(Foo&, Foo&). Nessun problema.

Ma cosa succede se Foo è una classe modello? Il C++ non ha una specializzazione parziale delle funzioni, quindi non è possibile specializzare swap. La tua unica scelta è il sovraccarico, ma lo standard dice che non ti è permesso aggiungere sovraccarichi nello spazio dei nomi std!

avete due opzioni a questo punto:

  1. creare una funzione swap(Foo<T>&, Foo<T>&) nel proprio spazio dei nomi, e sperare che venga trovata tramite ADL. Dico "speranza" perché se la libreria standard chiama swap come std::swap(a, b);, ADL semplicemente non funzionerà.

  2. Ignora la parte dello standard che dice di non aggiungere sovraccarichi e farlo comunque. Onestamente, anche se tecnicamente non è permesso, in tutti gli scenari realistici funzionerà.

Una cosa da ricordare però è che non c'è alcuna garanzia che la libreria standard utilizza swap a tutti. La maggior parte degli algoritmi utilizza std::iter_swap e in alcune implementazioni che ho esaminato, non sempre inoltra a std::swap.

+0

grande esempio signore – tenfour

+6

"dico speranza perché" ... Questo punto è stato notato nel WG21 diversi anni fa. Tutti gli implementatori di librerie standard non lo sanno. – MSalters

+0

Risposta meravigliosa, grazie mille. –

5

Non è consentito sovraccaricare le funzioni nello spazio dei nomi std, ma è possibile specializzare i modelli (come ricordo), quindi questa è un'opzione.

L'altra opzione consiste nel mettere la funzione swap nello stesso spazio dei nomi su cui sta operando e using std::swap; prima di chiamare uno scambio non qualificato.

+0

Preferisco usare 'std :: swap;' per 'usando lo spazio dei nomi std;'. –

+0

È possibile specializzare alcuni modelli: _ "Un programma può aggiungere una specializzazione di modello per qualsiasi modello di libreria standard allo std dei nomi solo se la dichiarazione dipende da un tipo definito dall'utente e la specializzazione soddisfa i requisiti di libreria standard per il modello originale e non lo è esplicitamente proibito. "_ [n3337; 17.6.4.2.1/1] Quindi è possibile specializzare 'std :: swap' per i tipi non in' std'. – boycy

+0

perché non è possibile sovraccaricare le funzioni std? è una cattiva abitudine ma lo spazio dei nomi std non è speciale. Non mi piace, ma molti prodotti aggiungono std swap per i loro tipi nello spazio dei nomi std. – Nick

10

C'è poco da aggiungere alla risposta di Peter Alexander. Consentitemi di citare un solo uso in cui la specializzazione delle funzioni potrebbe essere preferibile a un sovraccarico: se è necessario selezionare tra le funzioni senza parametri.

E.g.

template<class T> T zero(); 
template<> int zero() { return 0; } 
template<> long zero() { return 0L; } 

di fare qualcosa di simile con l'overloading di funzioni, si dovrà aggiungere un parametro per la firma della funzione:

int zero(int) { return 0; } 
long zero(long) { return 0L; } 
+1

Uso interessante :) –