Question

J'écris un programme sous MS Visual C ++ 6.0 (oui, je sais qu'il est ancien, non, je ne peux rien faire pour mettre à niveau). Je vois des comportements que je trouve vraiment bizarres. J'ai une classe avec deux constructeurs définis comme ceci:

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

Lorsque j'instancie une instance de cette classe avec cette syntaxe: MyClass("blah"), elle appelle le premier constructeur. Comme vous pouvez le constater, j’y ai ajouté le mot-clé explicit dans l’espoir que cela ne ferait pas cela ... pas de dés. Il semblerait que la conversion de const char * à bool soit préférable à la conversion en RWCString, qui possède un constructeur de copie prenant un strPath. Pourquoi fait-il cela? Je supposerais qu'étant donné deux choix possibles comme celui-ci, cela dirait que c'est ambigu. Que puis-je faire pour l'empêcher de faire cela? Dans la mesure du possible, j'aimerais éviter de devoir transtyper explicitement l'argument <=> en <=>, car il sera utilisé fréquemment avec les littéraux, ce qui représente beaucoup de dactylographes supplémentaires (plus une erreur très facile à utiliser). faire).

Était-ce utile?

La solution

Explicit n’aidera pas ici car le constructeur ne fait pas partie de la conversion implicite, mais seulement le destinataire.

Il n’existe aucun moyen de contrôler l’ordre privilégié des conversions, mais vous pouvez ajouter un second constructeur prenant un caractère const *. E.g:

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

Autres conseils

Andrew Grant a fourni la solution. Je veux vous dire pourquoi cela ne fonctionne pas comme vous l'avez essayé. Si vous avez deux fonctions viables pour un argument, celle qui correspond le mieux à l'argument est appelée. La seconde nécessite une conversion définie par l'utilisateur, tandis que la première nécessite uniquement une conversion standard. C'est pourquoi le compilateur préfère le premier au second.

Si vous ne voulez pas continuer à le lancer, il me semble que vous devrez peut-être créer un autre ctor prenant un caractère constant *.

C'est ce que je ferais probablement dans cette situation.

(Vous ne savez pas pourquoi vous créez un ctor avec un type que vous ne transmettez pas pour la plupart de ses utilisations.)

modifier:

Je vois quelqu'un d'autre déjà posté cela pendant que je tapais le mien

Vous ne savez pas pourquoi il devrait confondre une référence à une chaîne et à un bool? J'ai vu des problèmes avec un bool et un int.
Pouvez-vous perdre la valeur par défaut pour le premier constructeur? Peut-être, puisque ceci en fait-il le constructeur par défaut de MyClass (), il est également celui par défaut s'il ne peut pas correspondre aux arguments

Vous avez le choix d’ajouter un constructeur qui prend explicitement un caractère constant *

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

Ou faire

MyClass( string("blah") );

Le compilateur sait intrinsèquement comment transformer un const char * en bool. Il faudrait voir si, pour l’un des types de premier argument des constructeurs MyClass, il existe un constructeur qui prend le type de source que vous lui avez attribué, ou s’il peut le convertir en un type accepté par l'un des constructeurs de l'un des types de premier argument de vos constructeurs MyClass ou ... eh bien, vous voyez où cela se passe et ce n'est que pour le premier argument. De cette façon se trouve la folie.

Le mot clé explicit indique au compilateur qu'il ne peut pas convertir implicitement une valeur du type de l'argument en un objet de votre classe, comme dans

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

C ++ a un ensemble de règles pour déterminer quel constructeur doit être appelé dans votre cas. Je ne le sais pas de mémoire, mais vous pouvez voir intuitivement que les arguments par défaut conduisent à l'ambiguïté.

Vous devez réfléchir à ces valeurs par défaut.

Vous pouvez utiliser certaines méthodes de construction statiques, nommées. Ou vous pouvez utiliser une classe différente (ce qui n'est pas un mauvais choix du point de vue de la conception). De toute façon, vous laissez le code client décider du constructeur à utiliser.

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

ou

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

Êtes-vous certain que le premier constructeur est appelé? Est-ce que vous l'appelez avec la chaîne codée en dur, ou est-ce caché derrière un #define? Êtes-vous sûr que le #define est ce que vous pensez? (Essayez de compiler avec l'option / Ef pour obtenir la sortie développée du préprocesseur et voir si l'appel ressemble à ce que vous attendez.)

EDIT: Sur la base de ce commentaire et d’autres, ma suggestion est d’ajouter un autre constructeur pour const char*. Est-ce faisable?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top