14

Questa domanda è ispirata allo Issue with std::reference_wrapper. Diciamo, ad esempio, operator< per std::vector. E 'definita come modello funzionePerché i contenitori standard utilizzano i modelli di funzione anziché gli operatori Koenig non modello

template< class T, class Alloc > 
bool operator<(const vector<T,Alloc>& lhs, 
       const vector<T,Alloc>& rhs); 

Di conseguenza, conversione implicita di funzione argomento nel tipo di parametro funzione corrispondente viene negato (sostanzialmente a causa della sua natura template). Ciò riduce notevolmente l'utilità e la convenienza di std::reference_wrapper. Ad esempio, non è possibile utilizzare std::sort su std::vector<std::reference_wrapper<std::vector<int>>>.

D'altra parte, tutti i problemi sono risolti solo se operator< è definito come un non-modello di operatore Koenig come

template <...> 
class vector ... { 
    friend bool operator<(const vector& a, const vector& b) {...} 
}; 

mi chiedo il motivo per cui la libreria standard ha adottato il primo approccio al posto di questo ?

+0

@jxh La differenza cruciale tra i due è: uno è un modello, che vieta effettivamente la conversione implicita, mentre l'altro non lo è, consentendo la conversione implicita. – Lingxi

+4

Bene, nel momento in cui l'operatore <'è stato standardizzato,' reference_wrapper' non esisteva, AFAIK. –

+2

Informazioni sulla parola: non ci sono operatori Koenig. Ci sono, invece, funzioni che vengono trovate dalla ricerca ADL/Koenig. – edmz

risposta

1

Considerate questo codice (Ah):

template <class T> 
class A { 
    public: 
    T m_x; 

    friend bool operator<(const A & lhs, const A & rhs) { 
    return lhs.m_x < rhs.m_x; 
    } 
}; 

E main.cpp:

#include "A.h" 

namespace buddy { 
bool operator<(const A<double> & lhs, const A<double> &rhs) { 
    return lhs.m_x > rhs.m_x; 
}; 
} 
using namespace buddy; 
int main(int argc, char ** argv) { 

    A<double> a1; 
    A<double> a2; 

    a1 < a2; 

    return 0; 
} 

Questo codice non compila:

main.cpp: 14: 5: errore: sovraccarico ambiguo per "operatore <" (i tipi di operando sono "A" e "A") a1 < a2;

Il motivo è ovviamente che entrambi gli operatori < sono corrispondenze esatte. D'altra parte, se cambiamo il primo operatore < a (definito al di fuori della classe):

template <class T> 
bool operator<(const A<T> & lhs, const A<T> & rhs) { 
    return lhs.m_x < rhs.m_x; 
} 

Il compilatore si ferma lamentarsi: ora è una gara tra una corrispondenza esatta, e un modello di funzione, in modo che il corrispondenza esatta si usa.

Se l'operatore < è stato definito nel modo che si sta suggerendo, non ci sarebbe alcun modo ragionevole per gli utenti di std :: vector di ridefinire il comportamento dell'operatore <, a meno di specializzare std :: vector, che è molto più lavoro.

In conclusione, gli scrittori standard hanno scelto di semplificare il sovraccarico dell'operatore <, anziché fornire un operatore < che potrebbe essere più utile in determinate situazioni. Penso che abbiano fatto la scelta giusta.

+1

* "non ci sarebbe alcun modo ragionevole per gli utenti di std :: vector di ridefinire il comportamento dell'operatore <" * Sembra che implichi che questa sia una buona cosa. Non penso che sarei d'accordo. Ad esempio, gli algoritmi generici nella Libreria standard consentono il passaggio di un oggetto funzione comparatore, per specificare una relazione ordine personalizzata. – dyp

+0

Non c'è nulla di particolarmente fondamentale nell'operatore

+0

Alexander Stepanov, uno degli inventori del SGI STL, sarebbe sicuramente in disaccordo: 'operatore <' e 'operator ==' (e la loro semantica specifica di un rigoroso ordinamento debole e uguaglianza) sono richiesti per fare un tipo * regular *. Ad esempio, 'operator <' è necessario per l'ordinamento e la ricerca rapida (ricerca binaria). Pertanto, non penso che questa "scappatoia" di ridefinizione dell'operatore '' sia stata introdotta intenzionalmente. – dyp