Pregunta

Soy un desarrollador de C ++ principiante y tengo una pregunta sobre toString y ostream Integración del operador a través de plantillas. Tengo ese código:

    struct MethodCheckerTypes{
        typedef unsigned char TrueType;
        typedef long FalseType;
    };
    template<typename T>struct HasToString{
        template<typename K>static typename MethodCheckerTypes::TrueType test(decltype(&K::toString));
        template<typename> static typename MethodCheckerTypes::FalseType test(...);
        static const bool value = sizeof(test<T>(0)) == sizeof(typename MethodCheckerTypes::TrueType);
        typedef decltype(test<T>(0)) ValueType;
    };

    template<typename T, typename K> struct IfDef{};
    template<> struct IfDef<typename MethodCheckerTypes::TrueType, ostream&>{
        typedef ostream& type;
    };

    class WithToString{
    public:
        string toString() const{
            return "hello";
        }
    };

    template<typename F, typename CharT, typename Traits> typename IfDef<typename HasToString<F>::ValueType, basic_ostream<CharT, Traits>&>::type operator<<(basic_ostream<CharT, Traits>& out, const F &ref){
        out<<ref.toString().c_str();
        return out;
    }
int main(int argc, char **argv){
    WithToString hasToString;
    cout<<hasToString<<endl;
    return 0;
}

El código se ha compilado sin errores, y la aplicación se ejecutó con éxito. ¿Es bueno usar tal enfoque? Quería implementarlo sin ayuda de Boost.

¿Fue útil?

Solución

El enfoque para implementar operator<< por sí mismo es normal. Pero usar partes del lenguaje que no comprende es una mala práctica (no creo que un principiante pueda escribir dicho código). Tiene dos alternativas: implementar un toString función miembro o sobrecarga operator<<(std::ostream&, T). El último enfoque le permite usar boost::lexical_cast para convertir un objeto a una cadena. En cuanto a mí, el último enfoque es más c ++ ish, si puede hacer algo sin una función miembro, es mejor hacerlo.

Otros consejos

Me acerqué usando el operador de transmisión de sobrecarga de soluciones de @Begemoth << y luego agregué un método de conveniencia "MIXIN" CLASE "TOSTRING" en los casos en que desee una cadena inmediata, por ejemplo para la depuración:

template<typename T>
class ToString
{
public:
    std::string toString() const
    {
        return convertToString(static_cast<const T&>(*this));
    }
};

template<typename T>
inline std::string convertToString(const T& value)
{
    std::stringstream s;
    s << value;
    return s.str();
}

Luego puede heredar de esta clase en los casos que tiene operador Ostream y le dará un método de tostración en esa clase. Si está utilizando BOOST, puede reemplazar la implementación de ConvertToString usando LEXICAL_CASCT:

template<typename T>
inline std::string convertToString(const T& value)
{
    return boost::lexical_cast<std::string>(value);
}

Si su clase está diseñada para la herencia y el acceso polimórfico a través de punteros, entonces la solución anterior no funcionará, ya que las plantillas están atadas en el momento de la compilación, por lo que se necesita un enfoque diferente. La siguiente clase de mezcla se puede usar en lugar de "tostring" arriba donde se define un método virtual. Luego se proporciona una macro de conveniencia para implementar la función virtual en términos de la función de plantilla "ConvertToString". Esto debe agregarse al cuerpo de la clase de cada clase derivada, ya que debe anularse y el puntero "este" vinculado estáticamente al tipo actual.

class ToStringVirtual
{
public:
    virtual ~ToStringVirtual() {}
    virtual std::string toString() const = 0;
};

#define IMPLEMENT_ToStringVirtual public: std::string toString() const override { return convertToString(*this); }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top