2010-04-01 30 views
9

Estoy intentando escribir una clase de host basada en políticas (es decir, una clase que hereda de su clase de plantilla), con un giro, donde la clase de política también está modelada por el clase de host, para que pueda acceder a sus tipos. Un ejemplo en el que esto podría ser útil es cuando una política (utilizada como mixin, en realidad) aumenta la clase de host con un método de clon polimórfico(). He aquí un ejemplo de un mínimo de lo que estoy tratando de hacer:Mezcla de diseño basado en políticas con CRTP en C++

template <template <class> class P> 
struct Host : public P<Host<P> > { 
    typedef P<Host<P> > Base; 
    typedef Host* HostPtr; 
    Host(const Base& p) : Base(p) {} 
}; 

template <class H> 
struct Policy { 
    typedef typename H::HostPtr Hptr; 
    Hptr clone() const { 
    return Hptr(new H((Hptr)this)); 
    } 
}; 

Policy<Host<Policy> > p; 
Host<Policy> h(p); 

int main() { 
    return 0; 
} 

Esto, por desgracia, falla al compilar, en lo que parece a mí como tipo circular dependencia:

try.cpp: In instantiation of ‘Host<Policy>’: 
try.cpp:10: instantiated from ‘Policy<Host<Policy> >’ 
try.cpp:16: instantiated from here 
try.cpp:2: error: invalid use of incomplete type ‘struct Policy<Host<Policy> >’ 
try.cpp:9: error: declaration of ‘struct Policy<Host<Policy> >’ 
try.cpp: In constructor ‘Host<P>::Host(const P<Host<P> >&) [with P = Policy]’: 
try.cpp:17: instantiated from here 
try.cpp:5: error: type ‘Policy<Host<Policy> >’ is not a direct base of ‘Host<Policy>’ 

Si alguien puede detectar una error obvio, o ha mezclado con éxito CRTP en políticas, agradecería cualquier ayuda.

Respuesta

6

De hecho, el problema se debe a la declaración HostPtr que aún no se ha visto al heredar de la política. Existe alguna discusión sobre la semántica exacta donde estas declaraciones son visibles por plantillas instanciadas, que tiene problemas bastante complejos, vea this defect report.

Pero en su caso, la situación es clara: antes del cuerpo de la clase, ningún código puede ver ninguna declaración de los miembros de la clase, por lo que su código falla. Se podía pasar el tipo como un parámetro de plantilla

template <template <class,class> class P> 
struct Host : public P<Host<P>, Host<P>* > { 
    typedef P<Host<P> > Base; 
    Host(const Base& p) : Base(p) {} 
}; 

template <class H, class Hptr> 
struct Policy { 
    typedef Hptr HostPtr; 
    HostPtr clone() const { 
    return Hptr(new H((Hptr)this)); 
    } 
}; 

Si hay más tipos, puede decidir pasar un rasgo

template <class Host> 
struct HTraits { 
    typedef Host *HostPtr; 
    // ... 
}; 

template <template <class,class> class P> 
struct Host : public P<Host<P>, HTraits< Host<P> > > { 
    typedef P<Host<P> > Base; 
    Host(const Base& p) : Base(p) {} 
}; 

template <class H, class Htraits> 
struct Policy { 
    typedef typename Htraits::HostPtr HostPtr; 
    HostPtr clone() const { 
    return Hptr(new H((Hptr)this)); 
    } 
};