Estoy usando mi clase como un parámetro de plantilla de una de sus clases principales, y esa clase principal lo usa en un argumento de plantilla (aunque sizeof()).Usar clase infantil como un parámetro de plantilla de una clase base y como un especificador de nombre anidado
y el compilador me da:
error: tipo incompleto 'invocador :: workerClass {alias} MyClass' utilizarse en el nombre anidado especificador
Sin embargo, la clase está bien definido en el archivo . Supongo que esto se debe a que la clase hija no está instanciada en el momento de la instanciación de la clase base, pero ese tipo de cosas ocurre con CRTP y no hay problemas.
La razón por la que uso la clase secundaria en el argumento de plantilla es hacer una llamada a función diferente si la clase secundaria tiene, o no tiene, una función específica.
Aquí hay un código mínimo para las pruebas
/* Structure similar to boost's enable if, to use
SFINAE */
template <int X=0, class U = void>
struct test {
typedef U type;
};
enum Commands {
Swim,
Fly
};
/* Structure used for template overloading,
as no partial function template specialization available */
template<Commands T>
struct Param {
};
template <class T>
class Invoker
{
public:
typedef T workerClass;
workerClass *wc() {
return static_cast<workerClass*>(this);
}
template <Commands command>
void invoke() {
invoke2(Param<command>());
}
/* If the child class has those functions, call them */
/* Needs template paramter Y to apply SFINAE */
template<class Y=int>
typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type
invoke2(Param<Fly>) {
wc()->fly();
}
template<class Y=int>
typename test<sizeof(Y)+sizeof(decltype(&workerClass::swim))>::type
invoke2(Param<Swim>) {
wc()->shoot();
}
template<Commands command>
void invoke2(Param<command>) {
/* Default action */
printf("Default handler for command %d\n", command);
}
};
template <class T, class Inv = Invoker<T> >
class BaseClass : public Inv
{
public:
template<Commands command>
void invoke() {
Inv::template invoke<command>();
}
};
class MyClass : public BaseClass<MyClass>
{
public:
void swim() {
printf("Swimming like a fish!\n");
}
/* void fly(); */
};
void testing() {
MyClass foo;
foo.invoke<Fly>(); /* No 'void fly()' in MyClass, calls the default handler */
foo.invoke<Swim>(); /* Should print the swimming message */
}
El error ocurre en la línea de:
typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type
Así que, ¿hay algún compilador que apoya esta, o se trata explícitamente especificado por la norma como un uso no válido de las plantillas? ¿Tengo que cambiar la forma en que estoy haciendo esto y encontrar un camino? CRTP me da esperanzas de que el código sea válido, pero no estoy seguro.
Si esto realmente no es posible, entonces ¿por qué exactamente y por qué funciona el CRTP?
Pues bien, el problema es claro, al menos: Usted está haciendo una definición recursiva autorreferencial: '' BaseClass requiere la completa 'MyClass' debido a la' decltype 'expresión, pero' MyClass' requiere una 'BaseClass ' completa como clase base. –
Necesita ocultar las especializaciones detrás de una capa adicional de direccionamiento indirecto para que no se evalúen como parte de la definición de la clase base. Póngalos en un tipo anidado privado y todo funcionará bien. – ildjarn
CRTP es complicado precisamente por esta razón, el problema al que se enfrenta es simple de entender: la plantilla base se instancia como parte de la declaración de clase, en un punto donde aún no está completa (piense que el tipo todavía depende de lo que la plantilla base puede agregar, por lo que no hay forma de que el compilador pueda saber cómo será el tipo derivado sin procesar primero la plantilla base --considere que la base podría agregar atributos de miembro que cambian el tamaño, o funciones de miembros virtuales que podrían cambiar el significado de las declaraciones de funciones en el tipo derivado) –