2009-03-19 4 views
3

Ich schreibe ein Programm unter MS Visual C++ 6.0 (ja, ich weiß, es ist alt, nein, ich kann nichts tun, um zu aktualisieren). Ich sehe ein Verhalten, das ich wirklich komisch finde. Ich habe eine Klasse mit zwei Konstrukteuren wie folgt definiert:falsche Argument Umwandlung bevorzugt beim Aufruf der Funktion

class MyClass 
{ 
public: 
    explicit MyClass(bool bAbsolute = true, bool bLocation = false) : m_bAbsolute(bAbsolute), m_bLocation(bLocation) { ; } 
    MyClass(const RWCString& strPath, bool bLocation = false); 

private: 
    bool m_bAbsolute; 
    bool m_bLocation; 
}; 

Wenn ich eine Instanz dieser Klasse mit dieser Syntax instanziiert: MyClass("blah"), ruft sie den ersten Konstruktor. Wie Sie sehen können, fügte ich das Schlüsselwort explicit hinzu, in der Hoffnung, dass es das nicht tun würde ... keine Würfel. Es scheint die Umwandlung von const char * zu bool gegenüber der Umwandlung zu RWCString vorzuziehen, die einen Kopierkonstruktor hat, der eine const char * nimmt. Warum macht es das? Ich würde annehmen, dass bei zwei möglichen Wahlmöglichkeiten, würde es sagen, es ist mehrdeutig. Was kann ich tun, um dies zu verhindern? Wenn es möglich ist, würde ich vermeiden wollen, dass das strPath Argument explizit in eine RWCString umgewandelt wird, da es sehr oft mit Literalen verwendet wird, und das ist eine Menge zusätzlicher Eingabe (plus ein wirklich einfacher Fehler zu machen).

Antwort

9

Explizit wird hier nicht helfen, da der Konstruktor nicht ein Teil der impliziten Konvertierung ist, nur der Empfänger.

Es gibt keine Möglichkeit, die bevorzugte Reihenfolge der Konvertierungen zu steuern, aber Sie könnten einen zweiten Konstruktor hinzufügen, der ein const char * verwendet. Z. B:

class MyClass 
{ 
public: 
    MyClass(bool bAbsolute = true, bool bLocation = false); 
    MyClass(const RWCString& strPath, bool bLocation = false); 
    MyClass(const char* strPath, bool bLocation = false); 

private: 
    bool m_bAbsolute; 
    bool m_bLocation; 
}; 
0

Nicht sicher, warum es einen Verweis auf eine Zeichenfolge und eine bool verwechseln sollte? Ich habe Probleme mit einem Bool und einem Int gesehen.
können Sie den Standardwert für den ersten Konstruktor verlieren - es da dies sein kann, dass es das Standard-Konstruktor für MyClass macht(), dann ist es auch der Standard, wenn es nicht die args mithalten kann

+0

Der C++ - Standard erlaubt es, jeden Zeiger in einen booleschen, IIRC umzuwandeln, weshalb const char * -> bool überhaupt erlaubt ist. Ich weiß einfach nicht, warum es das besser zu mögen scheint. Das Entfernen der Standard-Arg ist nicht ideal und würde wahrscheinlich keinen Unterschied machen, aber ich werde es versuchen. – rmeador

-1

Sind Sie bestimmte, dass es wirklich den ersten Konstruktor aufruft? Nennen Sie es mit der Zeichenfolge hart-codiert, oder ist es hinter einer #define versteckt? Sind Sie sicher, dass #define ist, was Sie denken, dass es ist? (Versuchen Sie, mit der Option/Ef zu kompilieren, um die erweiterte Präprozessorausgabe zu erhalten, und sehen Sie, ob der Aufruf so aussieht, wie Sie erwarten würden.)

EDIT: Basierend auf diesem und anderen Kommentaren, ist mein Vorschlag, einen weiteren hinzuzufügen Konstruktor für const char*. Ist das machbar?

+0

Ich rufe es mit einem bloßen String-Literal, keine #define, keine Konstante woanders definiert. Und ich bin ungefähr so ​​sicher, wie ich sein kann, dass es das erste aufruft, da das ist, wohin es springt, wenn ich im Debugger gehe :) – rmeador

1

Wenn Sie don t es Gießen behalten wollen, dann scheint es mir, dass Sie haben könnten, um eine andere Ctor zu machen, die * ein const char nimmt.

Das ist, was ich wahrscheinlich in dieser Situation tun würde.

(nicht sicher, warum Sie eine Ctor mit einer Art machen, dass Sie nicht für die meisten seiner Verwendung sind vorbei.)

edit:

Ich sehe schon jemand anderes diese während ich gepostet Eingabe wurde Mine

+0

meine Hoffnung war, dass der RWCString sowohl die Literal-Zeichenkette als auch den RWCString verarbeiten würde. Es wird hauptsächlich mit RWCString verwendet; Die Literale kommen nur mit einigen fest codierten (nicht meiner Auswahl) Konfigurationsdaten. – rmeador

0

Ihre Entscheidungen sind einen Konstruktor hinzuzufügen, die explizit nimmt einen const char *

MyClass(const char* strPath, bool bLocation = false); // Thanks Andrew Grant! 

Oder

Der Compiler weiß intrinsisch, wie man ein const char * in ein bool setzt.Es müsste gehen, um zu sehen, ob es für einen der Erstargumenttypen von MyClass-Konstruktoren einen Konstruktor gibt, der den Quelltyp nimmt, den Sie ihm gegeben haben, oder ob er ihn in einen Typ umwandeln kann, der von akzeptiert wird irgendeinen der Konstruktoren eines der Erstargumenttypen Ihrer MyClass-Konstruktoren, oder ... Nun, Sie sehen, wohin das geht und das ist nur für das erste Argument. So liegt der Wahnsinn.

0

Das explicit Schlüsselwort teilt den Compiler es kann keinen Wert des Arguments des Typs in ein Objekt der Klasse implizit konvertieren, wie in

struct C { explicit C(int i): m_i(i) {}; int m_i; }; 
C c = 10;//disallowed 
C c(2.5); // allowed 

C++ eine Reihe von Regeln hat zu bestimmen, welches Konstruktor aufgerufen werden soll in Ihrem Fall - ich weiß es nicht aus dem Hinterkopf, aber Sie können intuitiv sehen, dass die Standardargumente zu Mehrdeutigkeit führen.

Sie müssen diese Standards durch denken.

Sie können auf einige statische, benannte Konstruktionsmethoden zurückgreifen. Oder Sie können eine andere Klasse verwenden (was aus Designsicht keine schlechte Wahl ist). In beiden Fällen lässt der Clientcode entscheiden, welcher Konstruktor verwendet werden soll.

struct C { 
    static C fromString(const char* s, const bool b = true); 
    static C fromBools(const bool abs = true, const bool b = true); 
}; 

oder

struct CBase { 
    bool absolute; 
    bool location; 
    CBase(bool abs=true, bool loc=true); 
}; 

struct CBaseFromPath { 
    // makes 'absolute' true if path is absolute 
    CBaseFromPath(const char* s, const bool loc); 
}; 
6

Andrew Grant-brachte die Lösung. Ich möchte Ihnen sagen, warum es funktioniert nicht so, wie Sie es versucht haben. Wenn Sie zwei brauchbare Funktionen für ein Argument haben, wird derjenige aufgerufen, der dem Argument am besten entspricht. Die zweite erfordert eine benutzerdefinierte Konvertierung, während die erste nur eine Standardkonvertierung benötigt. Deshalb bevorzugt der Compiler die erste über die zweite.

Verwandte Themen