2010-12-18 7 views
12

Quiero que la clase derivada sea un descendiente de una clase de plantilla. Esa clase depende de los miembros del descendiente. En resumen Quiero este código para compilar:Uso de typedefs derivados de clase en base

struct IBootParams 
{ 
    virtual bool GetImmediate() = 0; 
}; 

template <class T> 
struct TBootBootParams 
{ 
    typename T::TransType transferType; 

    typename T::UseAbort_ useAbort; 

    bool GetImmediate() 
    { 
     if (transferType == T::e1) 
     { 
      return useAbort.someFlag; 
     } 

     return false; 
    } 

}; 

struct BootBootParams : public TBootBootParams<BootBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=0, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag;   

     char  someMember;    
     int   otherMember;   
    } useAbort; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

struct BootAltBootParams : public TBootBootParams<BootAltBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag;   

     long long  someMember;    
     long long  otherMember;   
    } useAbort; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    BootBootParams bp; 
    BootAltBootParams bpa; 

    bool f = bp.GetImmediate(); 
    f = bpa.GetImmediate(); 
} 
+0

¿Cuál es el error? – ybungalobill

+2

¿Por qué declaraste IBootParams? –

Respuesta

10

No puede hacerlo de esta manera. Cuando el compilador instancia TBootBootParams<BootBootParams>, aún no ha leído completamente la definición completa de BootBootParams, por lo que no puede acceder a sus miembros desde dentro de la definición de TBootBootParams (las funciones miembro de TBootBootParams son una excepción porque se crean instancias más adelante).

La solución habitual es tener una clase de rasgos:

template<class T> struct TBootBootParams_traits; 

template <class T> 
struct TBootBootParams 
{ 
    typename TBootBootParams_traits<T>::TransType transferType; 

    typename TBootBootParams_traits<T>::UseAbort_ useAbort; 

    bool GetImmediate() 
    { 
     if (transferType == TBootBootParams_traits<T>::e1) 
     { 
      return useAbort.someFlag; 
     } 

     return false; 
    } 

}; 

struct BootBootParams; 
template<> struct TBootBootParams_traits<BootBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag;   

     long long  someMember;    
     long long  otherMember;   
    }; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

struct BootBootParams : 
    public TBootBootParams<BootBootParams>, 
    public TBootBootParams_traits<BootBootParams> 
{ 
    UseAbort_ useAbort; 
}; 
+0

Este es un truco mejor que el mío, creo. Un poco más limpio. – Omnifarious

+0

Se ve increíble. Simplemente no entiendo por qué la declaración 'template <> struct TBootBootParams_traits' no tiene ningún parámetro de tipo dentro de' <> 'mientras que su declaración forward lo hace. –

+1

@Sergey: porque es una especialización. @All + @Omnifarious: es una manera canónica de pasar algunos parámetros a alguna plantilla. P.ej. STL lo usa para iteradores (iterator_traits). – ybungalobill

4

No hay manera de que esto funcione, que no es un poco de un truco. Así que aquí está mi intento de un poco de un truco:

struct IBootParams 
{ 
    virtual bool GetImmediate() = 0; 
}; 

template <class T> 
struct TBootBootParams : public IBootParams 
{ 
    typename T::TransType transferType; 

    typename T::UseAbort_ useAbort; 

    virtual bool GetImmediate() 
    { 
     if (transferType == T::e1) 
     { 
      return useAbort.someFlag; 
     } 

     return false; 
    } 

}; 

struct BootBootParams_types 
{ 
    enum SomeEnum 
    { 
     e1=0, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag; 

     char  someMember; 
     int   otherMember; 
    } useAbort; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

struct BootBootParams : public BootBootParams_types, 
         public TBootBootParams<BootBootParams_types> 
{ 
}; 

struct BootAltBootParams_types 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag; 

     long long  someMember; 
     long long  otherMember; 
    } useAbort; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

struct BootAltBootParams : public BootAltBootParams_types, 
          public TBootBootParams<BootAltBootParams_types> 
{ 
}; 

int main(int argc, const char * argv[]) 
{ 
    BootBootParams bp; 
    BootAltBootParams bpa; 

    bool f = bp.GetImmediate(); 
    f = bpa.GetImmediate(); 
} 
+1

+1 porque estuvo cerca :) – ybungalobill

+1

Gracias. ¡Interesante! – Romeno

0

Para que este método funcione, tiene que incluir el objeto de clase plantilla como un miembro de la clase padre.

2

hace esto lo que quieres? It compiles.

struct IBootParams 
{ 
    virtual bool GetImmediate() = 0; 
}; 

template <class T> 
struct TBootBootParams 
{ 
    bool GetImmediate() 
    { 
     if (static_cast<T*>(this)->transferType == T::e1) 
     { 
      return static_cast<T*>(this)->useAbort.someFlag; 
     } 

     return false; 
    } 

}; 

struct BootBootParams : public TBootBootParams<BootBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=0, 
     e2, 
     e3 
    } transferType; 

    struct UseAbort 
    { 
     bool  someFlag;   

     char  someMember;    
     int   otherMember;   
    } useAbort; 
}; 

struct BootAltBootParams : public TBootBootParams<BootAltBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    } transferType; 

    struct UseAbort 
    { 
     bool  someFlag;   

     long long  someMember;    
     long long  otherMember;   
    } useAbort; 
}; 

int main(void) 
{ 
    BootBootParams bp; 
    BootAltBootParams bpa; 

    bool f = bp.GetImmediate(); 
    f = bpa.GetImmediate(); 
} 
+0

Wow. Estoy realmente impresionado de que compila. ¿Se supone que compila? El hecho de que realmente funcione no significa que se supone que debe hacerlo. :-) – Omnifarious

+0

BTW, también se compila en gcc 4.5. – Omnifarious

+0

@Omnifarious: Sí, esto debería compilarse, es un CRTP directo. Me deshice de los miembros de la clase base que causaban el problema ... dado que su tipo de datos está determinado por el tipo derivado, pueden declararse en el tipo derivado, y la clase base puede acceder a ellos simplemente aprovechando el parámetro de la plantilla . –

1

probé un pequeño cambio a la solución de ybungalobill contar mis propias necesidades. Y esto lo que tengo

template<class T> 
struct TBootBootParams_traits; 

template <class T> 
struct TBootBootParams 
{ 
    typedef TBootBootParams_traits<T> Traits; 

    typename Traits::TransType transferType; 
    typename Traits::UseAbort_ useAbort; 

    bool GetImmediate() 
    { 
     if (transferType == TBootBootParams_traits<T>::e1) 
     { 
      return useAbort.someFlag; 
     } 

     return false; 
    } 
}; 

struct BootBootParams; 
struct BootAltBootParams; 

template<> 
struct TBootBootParams_traits<BootBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag;   

     long long  someMember;    
     long long  otherMember;   
    }; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

template<> 
struct TBootBootParams_traits<BootAltBootParams> 
{ 
    enum SomeEnum 
    { 
     e1=5, 
     e2, 
     e3 
    }; 

    struct UseAbort 
    { 
     bool  someFlag;   

     int someMember;    
     float otherMember;   
    }; 

    typedef SomeEnum TransType; 
    typedef UseAbort UseAbort_; 
}; 

struct BootBootParams : 
    public TBootBootParams<BootBootParams> 
{ 

}; 

struct BootAltBootParams : 
    public TBootBootParams<BootAltBootParams> 
{ 

}; 

int main(void) 
{ 
    BootBootParams bp; 
    BootAltBootParams bpa; 

    bool f = bp.GetImmediate(); 
    f = bpa.GetImmediate(); 
} 

Para resumir .. Yo uso una clase de plantilla como una opción imprescindible de otra clase para esta clase de colocar todos los datos del tipo de allí. Esto justo lo que necesitaba. Gracias de nuevo por la opción!

Cuestiones relacionadas