2010-07-29 19 views
6

tengo una clase de plantilla de C++ que expone una serie de métodos, por ejemplométodos Agregando especialización de plantilla

template<int X, int Y> 
class MyBuffer { 
public: 
    MyBuffer<X,Y> method1(); 
}; 

Ahora, Quiero exponer métodos adicionales para esta clase si X == Y. I haber hecho esto subclasificando MyBuffer,

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> { 
public: 
    MyRegularBuffer method2(); 
}; 

Ahora, el problema es que quiero ser capaz, por ejemplo

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> otherBuf = buf.method1().method2(); 

Pero no estoy seguro de cómo lograr esto. Traté de pensar en los constructores de copia, los operadores de conversión, etc., pero desafortunadamente mis habilidades en C++ están un poco oxidadas.

EDIT: Debo añadir que la creación de estos objetos es relativamente barato (y también, no va a suceder muy a menudo), lo que significa que estaría bien hacer algo como esto:

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> temp = buf.method1(); // Implicit conversion 
MyRegularBuffer<2> otherBuf = temp.method2(); 

El La pregunta es, entonces, ¿cómo puedo definir la conversión de esa manera? El operador de conversión necesita estar en MyBuffer, creo, pero quiero que esté disponible solo si X == Y.

+1

completamente incomprensible. Por ejemplo, usted habla sobre "el operador de conversión", pero no hay ninguno. Publica un código real. –

+0

@Neil, creo que el usuario ha hecho una pregunta genuina en la medida de sus posibilidades. Y creo que lo entiendo de alguna manera. –

+0

@ Aaron En ese caso, comparta su comprensión. –

Respuesta

5

usted no necesita una clase separada para representar el comportamiento especial. La especialización parcial le permite tratar algunas de las cajas MyBuffer < X, Y > especialmente y darles métodos adicionales.

Mantenga su declaración original de mybuffer < X, Y > y añadir:

template<int Y> 
class MyBuffer<Y, Y> { 
public: 
    MyBuffer<Y,Y> method1(); 
    MyBuffer<Y,Y> method2(); 
}; 

MyBuffer<1,2> m12; m12.method2(); // compile fail, as desired, as it doesn't have such a method because 1 != 2 
MyBuffer<2,2> m22; m22.method2(); // compile success 

Editar: mis últimas líneas no eran muy útil después de todo, como ha señalado Georg en los comentarios, así que los he borrado.

+1

El único inconveniente es que method1() tiene que volver a implementarse en MyBuffer , o de lo contrario el compilador se quejará de un método desconocido cuando intente llamar a MyBuffer :: method1(). AFAIK, no hay forma de que MyBuffer :: method1() delegue su implementación en MyBuffer :: method1() sin especificar diferentes parámetros de plantilla donde X! = Y. –

+0

Derivado de 'MyBuffer' no funcionaría - no lo hace 't conoce la clase derivada y, por lo tanto, no puede devolver el tipo apropiado de 'method1()'. –

+0

@Georg, no entiendo. ¿Puede ser más específico sobre qué línea fallará? He compilado mi código y funciona. Pero debo admitir que copié incorrectamente la última línea sobre "clase MyRegularBuffer". Se actualizará ahora. –

1

Es posible hacer lo que desee si method1 y method2 devuelven una referencia a *this. De lo contrario, tendrá que hacer una conversión o hacer que method1 sea virtual.

+0

Incluso si method1() lo devuelve, no puede llamar a method2() desde su valor de retorno, porque method2() no es parte de la interfaz de MyBuffer . – wilhelmtell

+0

¿Puede explicar el enfoque del método virtual? No estoy seguro de tener eso ... El problema que estoy enfrentando es el contrato, la implementación sería la misma (el objeto es idéntico en la memoria si X == Y, así que podría hacer una reinterpretación). – Krumelur

+0

wilhelmtell, sí, exactamente lo que quise decir. Fuiste más rápido :) – Krumelur

1

El truco es tener un MyRegularBuffer::method1 que llama MyBuffer::method1, a continuación, una forma de convertir la resultante MyBuffer<X,X> en un MyRegularBuffer<X>:

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> 
{ 
public: 

    MyRegularBuffer<X>() 
    {} 

    MyRegularBuffer<X>(MyBuffer<X,X>) 
    { 
    // copy fields, or whatever 
    } 

    MyRegularBuffer<X> method2(); 

    MyRegularBuffer<X> method1() 
    { 
    MyRegularBuffer<X> ret(MyBuffer<X,X>::method1()); 
    return(ret); 
    } 
}; 
+0

Gracias. Sí, esta es una buena idea, pero en mi caso no agrega nada a la solución de @Aaron McDaid, ya que tengo que implementar method1 nuevamente (method1 es en realidad una cantidad de métodos). – Krumelur

3

yo iría a CRTP aquí:

template<int X, int Y, class Derived> 
struct MyBufferBase { 
    // common interface: 
    Derived& method1() { return *static_cast<Derived*>(this); } 
}; 

template<int X, int Y> 
struct MyBuffer : MyBufferBase<X, Y, MyBuffer<X,Y> > { 
    // basic version 
}; 

template<int X> 
struct MyRegularBuffer : MyBufferBase<X, X, MyRegularBuffer<X> > { 
    // extended interface: 
    MyRegularBuffer& method2() { return *this; } 
}; 
+0

+1 Gracias por recordarme eso. –

+0

Gracias por eso. Esa es una buena idea. – Krumelur

+0

Hubiera marcado como respuesta si hubiera dado la marca a más de una respuesta, pero en este momento la solución de Aaron McDaid realmente se cae bastante bien, ya que mis métodos son bastante delgados (es una clase de envoltorio). – Krumelur

Cuestiones relacionadas