Pregunta

Estoy escribiendo un programa en MS Visual C ++ 6.0 (sí, sé que es antiguo, no, no hay nada que pueda hacer para actualizar). Estoy viendo un comportamiento que creo que es realmente extraño. Tengo una clase con dos constructores definidos así:

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

Cuando instancia una instancia de esta clase con esta sintaxis: MyClass("blah"), llama al primer constructor. Como puede ver, le agregué la palabra clave explicit con la esperanza de que no hiciera eso ... sin dados. Parece preferir la conversión de const char * a bool sobre la conversión a RWCString, que tiene un constructor de copia que toma un strPath. ¿Por qué hace esto? Supongo que dadas dos opciones posibles como esta, diría que es ambiguo. ¿Qué puedo hacer para evitar que haga esto? Si es posible, me gustaría evitar tener que emitir explícitamente el argumento <=> a un <=>, ya que se usará mucho con literales y eso es una gran cantidad de tipeo adicional (además de un error realmente fácil de hacer).

¿Fue útil?

Solución

Explícito no ayudará aquí ya que el constructor no es parte de la conversión implícita, solo el destinatario.

No hay forma de controlar el orden preferido de las conversiones, pero podría agregar un segundo constructor que tomó un const char *. Por ejemplo:

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

Otros consejos

Andrew Grant proporcionó la solución. Quiero decirte por qué no funciona de la manera que lo intentaste. Si tiene dos funciones viables para un argumento, entonces se llama la que mejor coincida con el argumento. El segundo requiere una conversión definida por el usuario, mientras que el primero solo necesita una conversión estándar. Es por eso que el compilador prefiere el primero sobre el segundo.

Si no quieres seguir lanzándolo, entonces me parece que quizás debas hacer otro ctor que tome un constante *.

Eso es lo que probablemente haría en esta situación.

(No estoy seguro de por qué está haciendo un ctor con un tipo que no está pasando la mayor parte de su uso).

editar:

Veo que alguien más ya publicó esto mientras escribía el mío

¿No está seguro de por qué debería confundir una referencia a una cadena y un bool? He visto problemas con un bool y un int.
¿Puede perder el valor predeterminado para el primer constructor? Puede ser que, dado que esto lo convierte en el constructor predeterminado para MyClass (), también es el predeterminado si no puede coincidir con los argumentos

Sus opciones son agregar un constructor que explícitamente tome un const char *

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

O hacer

MyClass( string("blah") );

El compilador intrínsecamente sabe cómo convertir un const char * en un bool. Tendría que buscar para ver si, para cualquiera de los tipos de constructores de MyClass de primer argumento, hay un constructor que tomará el tipo de fuente que le ha dado, o si puede convertirlo en un tipo que sea aceptado por cualquiera de los constructores de cualquiera de los tipos de primer argumento de sus constructores MyClass, o ... bueno, ya ve a dónde va esto y eso es solo para el primer argumento. De esa manera se encuentra la locura.

La palabra clave explicit le dice al compilador que no puede convertir un valor del tipo de argumento en un objeto de su clase implícitamente, como en

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

C ++ tiene un conjunto de reglas para determinar a qué constructor se debe llamar en su caso; no lo sé desde el fondo de mi cabeza, pero puede ver intuitivamente que los argumentos predeterminados conducen a la ambigüedad.

Debe pensar en esos valores predeterminados.

Puede recurrir a algunos métodos de construcción estáticos, con nombre. O puede usar una clase diferente (que no es una mala elección desde el punto de vista del diseño). De cualquier manera, deja que el código del cliente decida qué constructor usar.

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

o

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

¿Estás seguro de que realmente está llamando al primer constructor? ¿Lo estás llamando con la cadena codificada o está oculto detrás de un #define? ¿Estás seguro de que #define es lo que crees que es? (Intente compilar con la opción / Ef para obtener la salida expandida del preprocesador y ver si la llamada se ve como esperaría).

EDITAR: Basado en este y otros comentarios, mi sugerencia es agregar otro constructor para const char*. ¿Es eso factible?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top