14

Diese Frage wurde von Issue with std::reference_wrapper inspiriert. Beispiel: operator< für std::vector. Es wird als eine Funktionsschablone definiert alsWarum Standardcontainer Funktionsschablonen anstelle von Nicht-Vorlage-Koenig-Operatoren verwenden

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

Als Ergebnis implizite Umwandlung von Funktionsargument an den Typ des entsprechenden Funktionsparameter verweigert wird (im Allgemeinen wegen seiner Vorlage Natur). Dies reduziert die Nützlichkeit und den Komfort von std::reference_wrapper erheblich. Zum Beispiel können Sie std::sort unter std::vector<std::reference_wrapper<std::vector<int>>> nicht verwenden.

Auf der anderen Seite, alle Probleme sind nur zu lösen, wenn operator< als Nicht-Template-Koenig Operator wie

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

warum ich frage mich, definiert der Standard-Bibliothek der ehemaligen Ansatz statt dessen angenommen hat ?

+0

@jxh Der entscheidende Unterschied zwischen den beiden ist: Eine ist eine Vorlage, die implizite Konvertierung effektiv verbietet, während die andere nicht, erlaubt implizite Konvertierung. – Lingxi

+4

Nun, zu der Zeit, dass 'Operator <' genormt wurde, existierte 'reference_wrapper' nicht, AFAIK. –

+2

Über den Wortlaut: Es gibt keine Koenig-Betreiber. Es gibt stattdessen Funktionen, die durch ADL/Koenig-Lookup gefunden werden. – edmz

Antwort

1

Betrachten Sie diesen Code (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; 
    } 
}; 

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

Dieser Code nicht kompiliert:

main.cpp: 14: 5: Fehler: mehrdeutige Überlast für 'Operator <' (Operandentypen sind 'A' und 'A') a1 < a2;

Der Grund ist natürlich, dass beide der Operator < sind genaue Übereinstimmungen. Auf der anderen Seite, wenn wir den ersten Operator < auf (definiert außerhalb der Klasse) ändern:

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

Der Compiler hält beschweren: es ist jetzt ein Wettbewerb zwischen einer exakten Übereinstimmung und eine Funktionsvorlage, so dass die genaue Übereinstimmung wird eingesetzt.

Wenn Operator < in der Mode definiert wurde, die Sie vorschlagen, gäbe es keinen vernünftigen Weg für Benutzer von std :: vector, das Verhalten von Operator <, kurz von spezialisierten std :: vector selbst, neu zu definieren viel mehr Arbeit.

Zusammenfassend, die Standard-Autoren gewählt, um es leichter zu machen, Operator < zu überlasten, als einen Operator < bereitzustellen, der in bestimmten Situationen nützlicher sein könnte. Ich denke, sie haben die richtige Wahl getroffen.

+1

* "Es gibt keinen vernünftigen Weg für Benutzer von std :: vector, das Verhalten von operator neu zu definieren." Sie scheinen zu implizieren, dass dies eine gute Sache ist. Ich denke nicht, dass ich zustimmen würde. Zum Beispiel ermöglichen die generischen Algorithmen in der Standardbibliothek das Übergeben eines Komparatorfunktionsobjekts zum Spezifizieren einer benutzerdefinierten Ordnungsbeziehung. – dyp

+0

Es gibt nichts besonders Grundlegendes zum Operator

+0

Alexander Stepanov, einer der Erfinder der SGI STL, würde sicherlich nicht zustimmen: 'operator <' und 'operator ==' (und ihre spezifische Semantik einer strengen schwachen Ordnung und Gleichheit) sind erforderlich, um einen Typ * regulär * zu machen. Zum Beispiel ist 'operator <' notwendig zum Sortieren und schnellen Suchen (binäre Suche). Daher glaube ich nicht, dass diese "Lücke" der Neudefinition von "Operator" absichtlich eingeführt wurde. – dyp

Verwandte Themen