C++ CRTP y acceder a typedefs anidados derivados desde base
boost intrusive-containers (3)
Extendiendo la idea de @jpalecek , podríamos hacer que el argumento de la plantilla tome un argumento predeterminado. Pero necesitas habilitar C ++ 0x para obtener esto
#include <typeinfo>
#include <cstdio>
template<typename TWO>
class base
{
public:
template <typename X = TWO> // <-- (requires C++0x to have a default)
void foo(typename X::dummy& d)
{
printf("%s/n", typeid(d).name());
}
};
template<typename DUMMY>
class derived
: public base< derived<DUMMY> >
{
public:
typedef DUMMY dummy;
};
struct tag{};
int main()
{
derived<tag> foo;
tag t;
foo.foo(t); // <--- call the function like normal.
}
Edición: Pondré un enlace de github aquí cuando termine de modificar mi diseño para cualquier persona que esté interesada.
Fondo
Estoy reemplazando un boost::intrusive
, intrusive_set
, con mi propia implementación como relleno intrusivo compilado de 64 bits, punteros de 3 x 8 bytes en mis nodos contenedores. mi contenedor tiene un límite de 2 ^ 16 nodos, por lo que puedo reducirlo a 4 bytes por nodo con 2 ordinales de desplazamiento de 16 bits (que es una reducción de tamaño de 6x).
En el siguiente ejemplo, la base
es el contenedor de conjunto intrusivo. La clase derived
tiene un std::vector<container_entry_type<entry_type> >
. obviamente, con este nivel de indirección necesito tener un montón de typedef anidados en derivados, a los que me gustaría referirme en base.
ps, los contenedores son para el AST del lenguaje de descripción de datos. Los elementos contenidos son, por lo tanto, tipos de datos pequeños y 3 x 8 bytes es muy significativo. Especialmente porque los contenedores se utilizan para validar conjuntos de datos en bucles ajustados.
El problema aislado
Quiero lograr la siguiente semántica:
template<typename TWO>
class base
{
public:
void foo(typename TWO::dummy & d);
};
template<typename DUMMY>
class derived
: private base< derived<DUMMY> >
{
public:
typedef DUMMY dummy;
};
struct tag{};
int main()
{
derived<tag> foo;
}
Pero no puedo acceder a la typedef anidada desde la base. Esto es lo que tiene que decir Clang sobre el asunto:
main.cc: In instantiation of ‘base<derived<tag> >’:
main.cc:9:7: instantiated from ‘derived<tag>’
main.cc:20:16: instantiated from here
main.cc:5:8: error: no type named ‘dummy’ in ‘class derived<tag>’
En su lugar tengo que hacer:
template<typename type_key>
class traits
{
public:
typedef type_key dummy;
};
template<typename TWO, typename type_key>
class base
{
public:
void foo(typename traits<type_key>::dummy & d);
};
template<typename DUMMY>
class derived
: private base< derived<DUMMY>, DUMMY >
{
public:
typedef DUMMY dummy;
};
struct tag{};
int main()
{
derived<tag> foo;
}
¿Es esta la única manera de lograr mi caso de uso? Simplemente hace que las cosas sean mucho más verbosas. Supongo que los derivados también podrían derivarse de rasgos para guardar algunas pulsaciones de teclas.
Otra opción es no usar la derivación y conectar la lógica directamente a lo que se deriva actualmente. Sin embargo, me gustaría individualmente probar la base.
No hay necesidad de la clase de traits
. Puedes usar type_key
directamente en la base
.
Sin embargo, no puede evitar pasar el tipo explícitamente a la base
. En el momento en que se crea una instancia de la base
, el compilador aún no ha visto el typedef en derived
(más exactamente: la clase derived
aún no está completa, cómo podría hacerlo, dado que incluso su clase base aún no existe).
Otra posibilidad (que podría o no salvarle las pulsaciones de teclado) sería no usar los tipos anidados de las clases derivadas en el padre en algunos lugares. P.ej. en lugar de
void foo(typename TWO::dummy & d);
usarías
template <class T>
void foo(typename T& d);
Para obtener puntos adicionales, puede usar SFINAE para limitar realmente T
a los tipos permitidos para la variante original. (Tenga en cuenta que dentro de las plantillas anidadas, TWO::dummy
se puede usar libremente; solo se crea una instancia después de que se haya derived
todo lo incluido, por lo que funciona. En la versión ingenua, la derived
aún está incompleta en el momento de crear una instancia del base
con sus funciones miembro, no tiene ::dummy
, por lo que falla)