Pregunta

Tipo de pregunta aleatoria ...

Lo que estoy buscando es una forma de expresar una operación de conversión que utiliza un operador definido de la instancia de clase de la que estoy emitiendo, y genera un error en tiempo de compilación si no hay un operador de conversión definido para el tipo . Entonces, por ejemplo, lo que estoy buscando es algo como:

template< typename RESULT_TYPE, typename INPUT_TYPE >
RESULT_TYPE operator_cast( const INPUT_TYPE& tValue )
{
    return tValue.operator RESULT_TYPE();
}

// Should work...
CString sString;
LPCTSTR pcszString = operator_cast< LPCTSTR >( sString );

// Should fail...
int iValue = 42;
DWORD dwValue = operator_cast< DWORD >( iValue );

Nota al margen interesante: el código anterior bloquea el compilador C ++ VS2005 y no se compila correctamente en el compilador C ++ VS2008 debido a lo que supongo que es un error del compilador, pero espero que demuestre la idea.

¿Alguien sabe de alguna manera de lograr este efecto?

Editar: Más justificación, para explicar por qué podría usar esto. Supongamos que tiene una clase de contenedor que se supone que encapsula o abstrae un tipo, y lo está convirtiendo en el tipo encapsulado. Podrías usar static_cast & Lt; & Gt ;, pero eso podría funcionar cuando quisieras que fallara (es decir: el compilador elige un operador que puede convertir al tipo que pediste, cuando querías un error porque ese operador no está presente).

Es cierto que es un caso poco común, pero es molesto que no pueda expresar exactamente lo que quiero que haga el compilador en una función encapsulada ... de ahí la pregunta aquí.

¿Fue útil?

Solución

El código que publicó funciona con el compilador Cameau (que suele ser una buena indicación de que es C ++ válido).

Como sabe, un reparto válido consiste en no más de un reparto definido por el usuario, por lo que una posible solución en la que estaba pensando era agregar otro reparto definido por el usuario definiendo un nuevo tipo en la plantilla de conversión y tener un afirmación estática de que no hay conversión disponible del nuevo tipo al tipo de resultado (utilizando boost is_convertible ), sin embargo, esto no distingue entre operadores de reparto y constructores de reparto (ctor con un argumento) y permite realizar conversiones adicionales (por ejemplo, void* a bool). No estoy seguro de si hacer la distinción entre los operadores y los constructores es lo correcto , pero eso es lo que dice la pregunta.

Después de un par de días reflexionando sobre esto, simplemente puede tomar la dirección del operador de reparto. Esto es un poco más fácil decirlo que hacerlo debido al puntero peludo de C ++ a la sintaxis del miembro (me llevó mucho más tiempo del esperado para hacerlo bien). No sé si esto funciona en VS2008, solo lo revisé en Cameau.

template< typename Res, typename T>
Res operator_cast( const T& t )
{
    typedef Res (T::*cast_op_t)() const;
    cast_op_t cast_op = &T::operator Res;
    return (t.*cast_op)();
}

Editar: tuve la oportunidad de probarlo en VS2005 y VS2008. Mis hallazgos difieren de los del póster original.

  • En VS2008, la versión original parece funcionar bien (como la mía).
  • En VS2005, la versión original solo bloquea el compilador cuando se convierte desde un tipo integrado (por ejemplo, int int) después de proporcionar un error de compilación que no parece tan malo para mí y mi versión parece funcionar en todos los casos.

Otros consejos

Utilizando un constructor de conversión marcado explícito es cómo usted evitaría que el compilador permita que los tipos convertidos implícitamente inicialicen su clase de contenedor.

Como los mensajes de error del compilador relacionados con la plantilla suelen ser un problema completo para desentrañar, si no le importa especificar cada conversión, puede hacer que el compilador emita un mensaje más instructivo en el caso de error proporcionando también una definición de plantilla predeterminada. Esto utiliza el hecho de que el compilador solo intentará compilar código en plantillas que realmente se invocan.

#include <string>

// Class to trigger compiler warning   
class NO_OPERATOR_CONVERSION_AVAILABLE
{
private:
   NO_OPERATOR_CONVERSION_AVAILABLE(){};
};

// Default template definition to cause compiler error
template<typename T1, typename T2> T1 operator_cast(const T2&)
{
   NO_OPERATOR_CONVERSION_AVAILABLE a;
   return T1();
}

// Template specialisation
template<> std::string operator_cast(const std::string &x)
{
   return x;
}

suena como si quisieras especialización de plantilla, algo como esto haría:

/* general template */
template<typename T1, typename T2> T1 operator_cast(const T2 &x);

/* do this for each valid cast */
template<> LPCTSTR operator_cast(const CString &x) { return (LPCTSTR)x; }

EDITAR: como se señaló en otra publicación, puede poner algo en la versión general para darle un mensaje de error más útil si se realiza una conversión no compatible.

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