2010-02-17 4 views
24

Sto specializzando il 'less' (predicato) per un tipo di dati.Specializzazione di 'modello <class _Tp> struct std :: less' in diversi namespace

Il codice simile a questo:

template<> 
struct std::less<DateTimeKey> 
{ 
    bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const 
    { 
     // Some code ... 
    } 
} 

Quando si compila (g ++ 4.4.1 su Ubuntu 9.10), ottengo l'errore:

specializzazione di 'template struct std :: meno' in diversi namespace

ho fatto qualche ricerca e ha scoperto che c'era una 'soluzione' che ha coinvolto avvolgendo la specializzazione in un namespace std - vale a dire cambiare il codice a:

namespace std { 
template<> 
struct less<DateTimeKey> 
{ 
    bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const 
    { 
     // Some code ... 
    } 
} 
} 

che, in effetti, spegne il compilatore. Tuttavia, quella soluzione era da un post di 5 anni (dal 'grande' Victor Bazarof non meno [punk non intenzionale]). Questa soluzione è ancora la strada da percorrere, oppure esiste un modo migliore per risolverlo oppure la "vecchia maniera" è ancora valida?

+1

Sovraccarico 'DateTimeKey :: operator <'? – kennytm

risposta

23

Questo è ancora il modo per farlo. Sfortunatamente non è possibile dichiarare o definire funzioni all'interno di uno spazio dei nomi come si farebbe con una classe: è necessario racchiuderle in un blocco namespace.

+0

Ok, grazie per i chiarimenti. Andrò avanti e apporterò le modifiche –

3

Il meno funtore non deve essere in std namespace. So

struct A 
{ 
    A(int _v=0):v(_v){} 
    int v; 
}; 


template<> struct less<A> 
{ 
    bool operator()(const A& k1, const A& k2) const 
    { 
     return k1.v < k2.v; 
    } 
}; 


std::map<A,int> m; 
m[A(1)] = 1; 
m[A(2)] = 2; 

Funziona come previsto. (Chiama il funtore che hai appena creato).

Immagino che tu lo sappia già, ma puoi semplicemente scrivere il tuo operatore < (k1, k2), che è quello che cerca meno il functor predefinito.

bool operator<(const DateTimeKey & k1, const DateTimeKey & k2) 
{ 
//your code... 
} 
+3

+1 alla definizione di 'operatore <' invece di ridefinire meno –

23

Se è necessario specializzare un algoritmo standard, è possibile farlo nello spazio dei nomi std. È l'unica cosa che ti è permesso fare all'interno di quel namespace secondo lo standard.

[lib.reserved.names]/1

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library template results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the standard library requirements for the original template

Ora, la domanda è se si vuole realmente specializzarsi std::less. Si noti che std::less chiamerà l'operatore di confronto definito per il proprio tipo, quindi è possibile fornire tale operazione anziché specializzare il modello.

Il problema con la specializzazione std::less per il tipo specifico è che causerà confusione se si fornisce un'operazione diversa da quella eseguita da operator< per il proprio tipo. Se eseguono la stessa operazione, lascia la definizione predefinita std::less senza specializzazione.

Se non si desidera fornire l'operatore di confronto, ma si desidera comunque utilizzare il tipo in contenitori associativi o con algoritmi che richiedono un comparatore, è possibile fornire un funtore di confronto esterno con un altro nome che non confonderà altri lettori (e te stesso da qualche parte nel futuro).

+0

+1 per la citazione, e per entrambi rispondere alla domanda e offrire una buona alternativa. – JonM

3

Perché lo stai facendo?

std::less esiste soltanto per due scopi:

  1. per dare un nome all'operatore <, permettendogli di essere passato come functor
  2. consentire agli confrontando due puntatori che non sono nello stesso array (che è tecnicamente illegale se fatto con puntatori grezzi)

Non c'è motivo per un utente di sovraccaricarlo - sovraccaricare operator< o utilizzare una funzione di confronto personalizzata.

Ci sono algoritmi std che possono essere sensibilmente specializzati - std::swap è un buon esempio - e per farlo è necessario dichiarare la specializzazione all'interno dello spazio dei nomi std.

+2

Non sono sicuro che std :: swap sia un buon esempio, dato che la moneta intelligente è che non lo specializzi, definisci una funzione di 'scambio 'indipendente nello stesso spazio dei nomi dell'UDT, e lascia che sia ADL a trovarla. L'eccezione che posso pensare è se la tua classe deve lavorare con un modello che non ha mai ottenuto quel memo e che chiama 'std :: swap (t1, t2)' invece di 'usando std :: swap; swap (t1, t2); '. Non sono sicuro se questa sia una specializzazione ragionevole o semplicemente una specializzazione pragmatica ;-) –

+0

C'è stata molta discussione sulle mailing list di boost su quale sia la migliore, ed è per questo che boost :: swap prende in mano. –

+1

'std :: less' esiste per una terza ragione: a volte, l'operatore' di un tipo <'si comporta in modo leggermente strano. L'esempio principale è rappresentato da numeri in virgola mobile in cui si può considerare '-0.0' diverso da' 0.0', o dove si potrebbe voler rendere 'nan' comparabile. Altri esempi sono tipi che hanno solo un ordine parziale e dove la notazione '<' è una notazione specifica per dominio naturale. In questi casi, specializzare 'std :: less' consente di usare questo tipo in set e mappe. –

Problemi correlati