2011-06-17 16 views
6

Tengo un tipo de datos, por ejemplo, class Vector3. Ahora necesito crear varias clases con la misma interfaz que Vector3, pero con semántica de nivel superior (por ejemplo: Position, Velocity). Usar typedef no es suficiente, porque necesito que estos tipos sean distintos para que se puedan usar para sobrecargar. En C++ 0x probablemente podría usar la herencia del constructor:Envoltura semántica del tipo C++

struct Position: public Vector3 { 
    using Vector3::Vector3; 
}; 

¿Podría haber algún problema con esto? ¿Hay alguna forma mejor de hacerlo? ¿Es posible hacerlo sin usar las características C++ 0x y no tener que escribir explícitamente todos los constructores Vector3?

+0

El problema con esto es que las posiciones son convertir implícitamente a vectores (y que técnicamente hablando, Vector ahora deben tener un destructor virtual con el fin de ser utilizado con seguridad) – jalf

+0

@jalf : ¿Es realmente necesario el destructor virtual incluso cuando la clase derivada no agrega ningún dato o método? –

+0

jalf: Discuto el tema de las conversiones en mi respuesta – sehe

Respuesta

5

Considere el uso de una estructura de etiquetas

struct tagPosition {}; 
struct tagDirection {}; 
struct tagGeneric {}; 

namespace detail 
{ 
    template <typename Tag=tagGeneric> 
     class Vector3 
    { 
     // business as usual 
    }; 
} 

typedef detail::Vector3<tagPosition> Position; 
typedef detail::Vector3<tagDirection> Direction; 
typedef detail::Vector3<tagGeneric> Vector3; 

Para los puntos de bonificación, tienen los operadores de conversión/constructores:.

template <typename Tag=tagGeneric> 
     class Vector3 
    { 
     template <typename OtherTag> 
      explicit Vector3(const Vector3<OtherTag>& rhs) { /* ... */ } 

//  template <typename OtherTag> 
//   operator Vector3<OtherTag>() const { return /* ... */ } 
    }; 

Si te gusta vivir peligrosamente le puede descartar la palabra clave explicit o habilitar el operador de conversión implícita. Esto tendría el 'beneficio' de ser capaz de permitir que promiscuos resoluciones operador de este modo:

Position pos; 
Direction dir; 
Generic gen; 

dir = gen + pos; // you see why I call it 'promiscuous'? 

Me gustaría recomendar (lugar) para definir los operadores explícitas para casos como este (funciones gratuitas :)

Position operator+(const Position& v, const Translation& d) { /* .... */ } 

De esta forma, su modelo de clase refleja la semántica de sus clases.

C++0x would possibly contain things to enable explicit conversion operators, IIRC:

En el caso de los constructores conversión, puede desactivar las conversiones implícitas al declarar el constructor tan explícito El N1592 proposal extiende la semántica de esta palabra clave para todos los operadores de conversión. Un operador de conversión declarado explícito no realizará una conversión implícita. En su lugar, el programador tendrá que llamar explícitamente

+0

Si quiere ser matemáticamente correcto, solo debe permitir lo siguiente: Posición + Vector = Posición; Posición - Posición = Vector; Dirección == Vector; Dirección * Velocidad = Velocidad (¡La velocidad es un _scalar_ en unidades 1/hora!). Las posiciones no son estrictamente vectores, sino puntos en un espacio afín. Su "Dirección de velocidad *" es confusa: ambos son vectores, ¿qué significa su producto? –

+0

@Kerrek: No tenía tales suposiciones, y no son relevantes. La semántica en mi ejemplo no necesita ser correcta ni incorrecta. _ (además, ¿cómo puede decir primero que mi ejemplo es incorrecto, y solo después de eso pregúnteme qué significan las cosas? Tal vez la Dirección sea un Vector de Unidad por definición. Tenga en cuenta que la muestra no especifica cómo puede ** derivarse la Dirección ** de Vector3. Solo imagine que tiene las invariantes adecuadas por el bien del ejemplo_). -- ¡solo digo! – sehe

+0

@sehe: Has hecho de 'Velocity' un vector. Ahora, ¿qué podría significar Velocity times Direction? ¡Sólo curioso! –

4

Cuando he necesitado tipos diferenciados pero-similares que he usado generalmente un parámetro ficticio plantilla, como (fruto de la casualidad, sin tocar por las manos sucias del compilador)

template< class UniqueId > 
struct Blah {}; 

typedef Blah< struct AlphaId > Alpha; 
typedef Blah< struct BetaId > Beta; 

Esto puede o no puede ser Que necesitas.

Saludos & HTH,