Pregunta

Como ejercicio de aprendizaje, he estado observando cómo funciona la conversión automática de tipos en C++.I saber Por lo general, se debe evitar la conversión automática de tipos, pero me gustaría aumentar mis conocimientos de C++ entendiendo cómo funciona de todos modos.

he creado un StdStringConverter clase que se puede convertir automáticamente en una std::string, pero el compilador (g++ 4.3.4 en Debian) parece no realizar la conversión cuando el objeto se compara con un objeto real. std::string (ignore la falta de paso por referencia y la creación innecesaria de objetos temporales):

#include <string>

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

Por otro lado, si lo cambio ligeramente a un CStringConverter clase, la conversión automática hace tener lugar, aunque comparando char Los punteros probablemente no sean lo que pretendía:

#include <string>

class CStringConverter
{
public:
    explicit CStringConverter(std::string name) : m_name(name) {}
    operator const char* () const { return m_name.c_str(); }
private:
    std::string m_name;
};

int main()
{
    CStringConverter converter(std::string("Me"));
    const char* name = "Me";
    // Next line compiles fine, but they are not equal because the
    // pointers don't match.
    return (converter == name) ? 0 : 1;
}

¿Hay algo especial en la diferencia entre un std::string y un char* ¿En este contexto eso hace que el compilador no los trate igual?

¿Fue útil?

Solución

El problema se debe al hecho de que std :: string es en realidad una instancia de la plantilla de clase std :: basic_string. Un operador == que está disponible en el espacio de nombres std toma dos plantillas std :: basic_string:


template<class charT, class traits, class Allocator>
bool operator==(const basic_string& lhs,
                const basic_string& rhs);

Si esta versión del operador == se sobrecargara específicamente en std :: string, su código estaría bien. Pero ese no es el caso, lo que requeriría que el compilador realice una deducción de argumentos de plantilla en los parámetros de plantilla de std :: basic_string para que pueda entender que el retorno de su operador de conversión es una posible coincidencia.

Sin embargo, el compilador no hará eso. No sé qué parte de la norma establece esto con precisión. Pero la idea general es que tales conversiones funcionan solo para tipos que no son de plantilla.

Una cosa que puedo sugerir es que coloque StdStringConverter en un espacio de nombres y proporcione una versión del operador == para std :: string en ese espacio de nombres. De esta manera, cuando su compilador encuentra una expresión como esa, entra en juego ADL (búsqueda dependiente de argumentos) y todo funciona bien.


#include <string>

namespace n1 {

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator std::string () { return m_name; }
private:
    std::string m_name;
};

bool operator==(std::string const& a, std::string const& b)
{
  return a == b; //EDIT: See Paul's comment on std::operator== here.
}

}

int main()
{
    using namespace n1;
    StdStringConverter converter(std::string("Me"));
    std::string name = "Me";
    return (converter == name) ? 0 : 1;   
}

Otros consejos

En el primer ejemplo, las dos clases comparadas (string y StdStringConverter) no reciben ningún tratamiento especial del compilador para la conversión de tipos. Eso significa que la sobrecarga del operador que realizó ni siquiera se activa. El compilador revisa la lista de sobrecargas del operador == y ninguna de ellas toma un StdStringConverter para que te grite.

En el segundo ejemplo, el nombre es char *. Como es un tipo primitivo, el compilador intenta convertir lo no primitivo en un char *. Dado que tiene una anulación, funciona y compara direcciones.

El compilador no emitirá tipos explícitamente en operaciones que no incluyen tipos primitivos. Algo que hará es intentar usar constructores para hacer que los tipos coincidan. Por ejemplo, si cambia su primer ejemplo a esto:

#include <string>

class StdStringConverter
{
public:
    StdStringConverter(std::string name) : m_name(name) {}
    bool operator==(const StdStringConverter &name) { return m_name == name.m_name; }
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

Ahora el programa devuelve 0. Dado que el constructor no es explícito, el compilador intentará usarlo para convertir la cadena a StdStringConverter. Como ahora hay un operador == en StdStringConverter, todo funciona.

Hay múltiples factores. Si cambia la declaración de devolución de esta manera

return (std :: operator == (nombre, nombre))? 0: 1;

se compila, aunque obviamente no hace lo mismo. Por otro lado

return (std :: operator == (convertidor, nombre))? 0: 1;

no, pero proporciona un mensaje de error más interesante

ninguna función coincidente para la llamada a & # 8216; operator == (StdStringConverter & amp ;, const std :: string & amp;)

lo que me recuerda que operator == está en plantilla en basic_string < > ;, que tiene tres parámetros de plantilla para arrancar. Si usa int en su ejemplo en lugar de std :: string, el compilador no se queja.

Cómo obtener el efecto deseado con std :: string es más intrigante ...

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