Verwalten Implizite Typumwandlung in C ++
-
20-08-2019 - |
Frage
Ich arbeite an Code, die Nearest-Neighbor-Abfragen. Es gibt zwei einfache Ideen, die zu Grunde liegen, wie ein Benutzer Daten in einer Suchabfrage können:
- am nächsten N Punkte zu einem bestimmten Punkt im Raum.
- alle Punkte innerhalb einer bestimmten Entfernung.
In meinem Code, werden Punkte in einen Punktliste setzen, und die Punktliste ist ein Container die Aufgabe, die Verfolgung der Punkte hat, die bei der Suche gefunden wurde.
Im Moment meiner Punktliste Objekt hat einen Konstruktor:
PointList( unsigned int maxvals ); // #1
Die nächsten beiden Konstrukteure, die Ich mag würde hinzufügen, sind diese:
PointList( float maxdist ); // #2
PointList( unsigned int maxvals, float maxdist ); // #3
Meine Frage ist: Wie kann ich sicherstellen, dass meine Benutzer und der C ++ Compiler das Recht generieren Konstruktor für die Punktliste und unterscheidet zwischen Konstruktoren 1 und 2? Soll ich implementieren gerade # 3 und Konstanten zur Verfügung stellen, die großen willkürlich definieren Werte für maxvals und maxdist? Eine weitere Alternative könnte ein anderes System von leichten Gegenständen zu schreiben, die die Logik für das Hinzufügen von Punkten in die Liste regieren, aber das fühlt sich an wie viel des Guten für eine so einfache Idee.
Ich versuche wirklich für meine Benutzer transparent zu machen, die vor allem Wissenschaftler, die C ++ gelernt haben, manchmal ohne den Nutzen der formalen Bildung. Dank!
Lösung
Die Überladungsauflösung für Integer-Typen passiert auf zwei Kategorien, die sehr grob zusammengefasst werden können
- Promotion: Dies ist eine Konvertierung von Typen kleiner als
int
int
oderunsigned int
, je nachdem, obint
alle Werte des Quelltypen speichern kann. - Konvertierung. Dies ist eine Konvertierung von beliebigen ganzzahligen Typ in einen anderen Typ Integer
ähnliche, Konvertierung für Punkttypen Floating passiert auf zwei Kategorien
- Promotion: Dies ist eine Umwandlung von
float
zudouble
- Konvertierung: Dies ist eine Konvertierung von jedem Gleitkomma-Typ in einer anderen Gleitkommatyps
Und es gibt eine Umwandlung von Integer in Schwimm- oder zurück. Dies wird als eine Umwandlung gewählt, anstatt eine Förderung. Eine Förderung ist auf Platz besser als eine Umwandlung, und wo nur eine Förderung benötigt wird, wird dieser Fall bevorzugt werden. So können Sie die folgenden Konstrukteure verwenden
PointList( int maxVals );
PointList( unsigned int maxVals );
PointList( long maxVals );
PointList( unsigned long maxVals );
PointList( double maxDist );
PointList( long double maxDist );
Für jeden Integer-Typen, soll dies die erste Gruppe von Konstruktor wählen. Und für jeden Punkt schwebenden, soll dies die zweite Gruppe von Konstrukteuren wählen. Ihre ursprünglichen zwei Konstrukteuren leicht in einer Mehrdeutigkeit zwischen float
und unsigned int
führen kann, wenn Sie eine int
passieren, zum Beispiel. Für den anderen zwei Argumente Konstruktor, können Sie mit Ihrer Lösung gehen, wenn Sie wollen.
Das heißt, ich auch eine Fabrik-Funktion verwenden würde, weil von der Art des Parameters die Bedeutung der Entscheidung ziemlich zerbrechlich ist, denke ich. Die meisten Menschen würden erwarten, dass das folgende Ergebnis gleich
PointList p(floor(1.5));
PointList u((int)1.5);
Aber es in einem anderen Zustand führen würde.
Andere Tipps
Warum Factory-Methoden nicht statt Konstrukteure verwenden? Factory-Methoden haben den Vorteil, anpassbare Namen.
static PointList createNearestValues(unsigned int maxvals) {}
static PointList createByDistance(float maxdist) {}
Erwägen Sie wahr typedefs . Es ist ein wenig mehr Aufwand seitens des Client-Code, aber Sie sind garantiert Richtigkeit.
Anrufpunktliste (10) für die erste und die Punktliste (10f) für den zweiten.
Für die zweite, können Sie auch 10.0 verwenden.
Wenn Konstrukteure # 1 und # 2 vorhanden ist, der richtige Konstruktor wird aufgerufen, wenn der Wert, den Sie einfügen von float oder int und keine Umwandlung stattfinden soll. So sicher nur, dass Sie die Art der Zahlen machen Sie explizit aufrufen verwenden, um (das heißt 1f und 1). Constructor # 3 scheint nicht viel von einer Option zu sein, da es nicht wirklich notwendig ist, und würde nur Benutzer Ihres Codes verwirren. Wenn Sie die Standardwerte für die beiden Nummer benötigen könnten Sie
PointList(int max, float max=VALUE)
und
PointList(float max, int max=VALUE)
Auch hier: das scheint mehr Schaden dann Code in Bezug auf die Lesbarkeit des Codes zu tun
.Dies fordert eine gute Lektüre auf Überladungsauflösung .
Ich würde auf jeden Fall verwenden expliziten Konstrukteure. Im Beispiel ist die ganze Zahl ohne Vorzeichen konvertiert nicht implizit.
class A
{
public:
explicit A(float f){}
explicit A(int i){}
};
void test(){
unsigned int uinteger(0);
A a1(uinteger); //Fails, does not allow implicit conversions
A a2((float)uinteger); //OK, explicit conversion
float f(0.0);
A a3(f); //OK
int integer(0);
A a4(integer); //OK
}
Die Fehlermeldung ist leicht genug, um zu verstehen:
: error C2668: 'A::A' : ambiguous call to overloaded function
: could be 'A::A(int)'
: or 'A::A(float)'