2016-04-09 6 views
3

Es funktioniert für die struct xy, die ich erklärte. Warum funktioniert das gleiche Muster nicht für complex<int>?Warum "keine Übereinstimmung für 'Operator <'", als ich es erklärte?

#include <complex> 
#include <set> 
using namespace std; 

struct xy { 
    int x, y; 
}; 

bool operator< (const xy &a, const xy &b) { 
    return a.x < b.x; 
} 

bool operator< (const complex<int> &a, const complex<int> &b) { 
    return a.real() < b.real(); 
} 

int main() { 
    xy q; 
    set<xy> s; 
    s.insert(q); 

    complex<int> p; 
    set< complex<int> > t; //If I comment out these two lines, 
    t.insert(p);    //it compiles fine. 

    return 0; 
} 

Die Fehlermeldung:

 
In file included from c:\m\lib\gcc\mingw32\4.8.1\include\c++\string:48:0, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\locale_classes.h:40, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\ios_base.h:41, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\ios:42, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\istream:38, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\sstream:38, 
       from c:\m\lib\gcc\mingw32\4.8.1\include\c++\complex:45, 
       from test.cpp:1: 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h: In instantiation of 'bool less<>::operator()(const _Tp&, const _Tp&) const': 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1321:11: required from 'pair<> _Rb_tree<>::_M_get_insert_unique_pos(const key_type&)' 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1374:47: required from 'pair<> _Rb_tree<>::_M_insert_unique(_Arg&&)' 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_set.h:463:29: required from 'pair<> __cxx1998::set<>::insert(const value_type&)' 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\debug\set.h:220:59: required from 'pair<> __debug::set<>::insert(const value_type&)' 
test.cpp:28:19: required from here 
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h:235:20: 
error: no match for 'operator<' (operand types are 'const std::complex<int>' and 'const std::complex<int>') 
     { return __x < __y; } 

Meine beste Vermutung ist, dass dies etwas mit complex<T> ist eine Klasse zu tun hat, nicht eine Struktur. Ich kann nicht die Logik sehen, warum das einen Unterschied machen sollte. Oder ist es eine Schreckhaftigkeit der Vorlage?

Was ich sehe, passiert ist, dass die STL irgendwann versucht (grob gesagt) a < b zu tun, wo a und b sind complex<int> Instanzen. Es ist also auf der Suche nach bool operator< (const complex<int> &a, const complex<int> &b). Nun, es gibt genau das erklärt knapp über main(). Warum ist es unvernünftig? Ich dachte, vielleicht mochte es sie nicht, Referenzen zu sein. Aber das Entfernen der Ampersands machte keinen Unterschied zu seiner Beschwerde.

+0

Ich glaube, es hat mit "Punkt der Definition" und "Punkt der Instanziierung" zu tun. Ich bin einfach nicht in der Lage, mich an all die Feinheiten zu erinnern. Bitte sieh sie dir an. – Nawaz

+0

@Nawaz Meinst du die Regel, dass wir im Text des Programms eine Funktion deklarieren müssen * bevor * wir sie benutzen? (Nachdem der Präprozessor alle '# include's vorverarbeitet.) Das scheint Sinn zu ergeben, aber dann verstehe ich nicht, warum der Fall von' struct xy' funktioniert ... Vielleicht sind die Operatoren wie 'operator <' immer implizit erklärt? –

Antwort

2

Eine Option besteht darin, einen benutzerdefinierten Vergleichsfunktor zu schreiben und den Satz damit zu instanziieren.

#include <complex> 
#include <set>  

bool operator< (const std::complex<int> &a, const std::complex<int> &b) { 
    return a.real() < b.real(); 
} 

struct my_less { 
    bool operator() (const std::complex<int>& lhs, const std::complex<int>& rhs) const{ 
     return lhs < rhs; 
    } 
}; 

int main() { 
    std::complex<int> p; 
    std::set< std::complex<int>, my_less > t; 
    t.insert(p); 

    return 0; 
} 

Sample on Coliru

+2

es ist UB, 'operator <' für 'std :: complex' innerhalb' namespace std' hinzuzufügen – TemplateRex

+0

@TemplateRex Ok, stimmt, ich werde es nur auf den Funktor reduzieren. –

+1

Eine allgemeinere Regel, die ich gefunden habe, ist, dass das Hinzufügen von irgendetwas zu "namespace std" Undefiniertes Verhalten ist, mit einigen begrenzten Ausnahmen. Siehe [diese Antwort] (http://stackoverflow.com/q/25120423/1143274), die den relevanten Absatz aus dem Standard zitiert. –

2

Es funktioniert für die struct xy, die ich erklärt. Warum funktioniert das gleiche Muster nicht für complex<int>?

Der Grund dafür ist, dass, wenn Sie Arten von Namensraum std nur verwenden, wie std::set und std::complex, der Compiler keinen Grund für die Betreiber in allen anderen Namensräumen zu suchen hat.

Mit struct xy ist dies anders, da der Operator zusammen mit dem Typ deklariert wird.


Und auch der aktuelle Standard sagt:

Die Wirkung der Templat-Komplex für jeden anderen Typ als Schwimmer instanziiert wird, double oder long double ist nicht näher bezeichnet.

Die Verwendung von complex<int> funktioniert möglicherweise oder hängt nicht davon ab, welchen Compiler Sie verwenden. "Nicht spezifiziert" ist nicht so schlecht wie "undefiniert", aber natürlich nicht sehr zuverlässig oder tragbar.

+0

* "Der Compiler hat keinen Grund, nach Operatoren in anderen Namespaces zu suchen." * Das ist falsch. Der Compiler kann auch nach Namen in anderen Namespaces suchen, insbesondere im globalen Namespace. Alles hängt vom Definitionspunkt (und möglicherweise vom Instanziierungspunkt) ab. – Nawaz