2009-12-16 18 views
6

Considere el siguiente código¿Cómo guardar el puntero al miembro en tiempo de compilación?

template<typename T, int N> 
struct A { 
    typedef T value_type; // OK. save T to value_type 
    static const int size = N; // OK. save N to size 
}; 

Mira, es posible guardar cualquier parámetro de plantilla si este parámetro es un nombre de tipo o un valor entero. El problema es que puntero al miembro es un desplazamiento, es decir, entero. Ahora quiero guardar cualquier puntero a miembro en tiempo de compilación:

struct Foo { 
    int m; 
    int r; 
}; 

template<int Foo::*ptr_to_member> 
struct B { 
    // Next statement DOES NOT WORK! 
    static int Foo::* const saved_ptr_to_member = ptr_to_member; 
}; 

// Example of using 
int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    Foo foo; 
    std::cout << (foo.*(Bm::saved_ptr_to_member)); 
} 

cómo guardar puntero a miembro en tiempo de compilación ? Yo uso VS2008.

Nota. El tiempo de compilación es crítico. Por favor, no escriba solución en tiempo de ejecución. Lo sé.

+2

ja-ja, supongo Moonshadow aprendido algo nuevo hoy ... –

+0

@ Jon: de hecho, acaba de descubrir chapeter 15.5: Los punteros a los miembros de mi referencia BS. * tímido * – moonshadow

+0

acababa de escribir una oración exactamente de esa sección cuando me di cuenta de que había borrado su respuesta. Aunque creo que su nombre es confuso, en realidad no son nada como punteros. –

Respuesta

0

¿Por qué utilizar una plantilla?

#include <cstdio> 

struct Foo { 
    int a; 
    int b; 
} foo = {2, 3}; 

int const (Foo::*mp) = &Foo::b; 

int 
main() { 
    printf("%d\n", foo.*mp); 
    return 0; 
} 

El siguiente compila mp a esto en gcc-4.4.1 (no tengo acceso a MSVC por el momento):

.globl mp 
     .align 4 
     .type mp, @object 
     .size mp, 4 
mp: 
     .long 4 

Es sólo un desplazamiento al miembro, que parece bastante tiempo de compilación para mí.

Con plantilla, es necesario que especifique la definición fuera de la clase:

#include <cstdio> 

struct Foo { 
    int m; 
    int r; 
} foo = {2, 3}; 

template<int Foo::*Mem> 
struct B { 
    static int Foo::* const mp; 
}; 

template<int Foo::*Mem> 
int Foo::* const B<Mem>::mp = Mem; 

int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    printf("%d, %d\n", foo.*(Bm::mp), foo.*(Br::mp)); 
} 

que compila a:

g++ -O2 -S -o- b.cc | c++filt 

... 

     .weak B<&(Foo::r)>::mp 
     .section  .rodata._ZN1BIXadL_ZN3Foo1rEEEE2mpE,"aG",@progbits,B<&(Foo::r)>::mp,comdat 
     .align 4 
     .type B<&(Foo::r)>::mp, @object 
     .size B<&(Foo::r)>::mp, 4 
B<&(Foo::r)>::mp: 
     .long 4 
     .weak B<&(Foo::m)>::mp 
     .section  .rodata._ZN1BIXadL_ZN3Foo1mEEEE2mpE,"aG",@progbits,B<&(Foo::m)>::mp,comdat 
     .align 4 
     .type B<&(Foo::m)>::mp, @object 
     .size B<&(Foo::m)>::mp, 4 
B<&(Foo::m)>::mp: 
     .zero 4 

Sin embargo, esto todos los tortazos de la biblioteca estándar cuenta con reimplementación (ver std::tr1::mem_fn).

+1

Simplemente no entiendo mi pregunta. –

+0

Luego, aclare su pregunta, mucha gente parece estar en el mismo barco. –

+0

No se ofenda. Quiero guardar puntero a miembro en la clase de plantilla como un tipo o un entero. Necesito * un puntero a rasgos de miembro * como rasgos de tipo. –

1

No puede.

Pero puede usar un functionoid en su lugar. Este puede ser una solución en tiempo de compilación. Y debido a que el compilador puede alinear cosas, es posiblemente incluso más rápido que un puntero a una función de miembro. Ejemplo:

struct Foo { 
    int m; 
    int r; 
}; 

struct FooM { 
    static int call(Foo const &foo) const { return foo.m; } 
} 

struct FooR { 
    static int call(Foo const &foo) const { return foo.r; } 
} 

template<typename FooFun> 
struct B { 
    typedef FooFun foo_fun; 
    int call_foo_fun(Foo const &foo) { return foo_fun::call(foo); } 
}; 

// Example of using 
int main() { 
    typedef B<FooM> Bm; 
    typedef B<FooR> Br; 
    Foo foo; 
    std::cout << Bm.call_foo_fun(foo); 
} 

No probado, pero ya se ha hecho.

0

No puede inicializar un miembro estático dentro de la definición de una estructura. Tiene que ser declarado fuera así (que probablemente no es lo que pretende con la plantilla, pero de todos modos):

struct Foo { 
    int m; 
    int r; 
}; 

template<int Foo::*ptr_to_member> 
struct B { 
    static int Foo::* const saved_ptr_to_member; 
}; 

int Foo::* const B<&Foo::m>::saved_ptr_to_member = &Foo::m; 
int Foo::* const B<&Foo::r>::saved_ptr_to_member = &Foo::r; 

// Example of using 
int main() { 
    typedef B<&Foo::m> Bm; 
    typedef B<&Foo::r> Br; 
    Foo foo; 
    std::cout << (foo.*(Bm::saved_ptr_to_member)); 
} 
+0

1) Es una solución en tiempo de ejecución. 2) Este método no usa las ventajas de las plantillas. Debo definir un nuevo puntero a miembro cada vez para el caso de una nueva estructura y miembros. –

+0

1) no, no lo es. 2) cierto, pero ese fue el diseño original, ahora escrito de una manera que el compilador lo entiende. No veo ninguna razón para usar una plantilla aquí en primer lugar, simplemente conviértalo en una clase con un miembro no estático y un constructor descender y listo. Mucho más simple ... – user231967

1

Sería bueno tener una explicación más elaborada de por qué 'tiempo de compilación es importante' (ayuda a sugerir alternativas). Pero para mi noción de todo lo que necesita para hacer el tiempo de compilación con puntero-a-miembro, en realidad puede hacer. Mi variante es la sugerencia de Thomas combinada con cierta filosofía de tipo C++. En primer lugar permite definir:

template <typename T, T v> 
struct val 
{}; 

esta plantilla estructura puede servir efectivamente como valor de tiempo de compilación, y que no es necesario "valor estático = v;", para usarlo, ya sea en tiempo de compilación o en tiempo de ejecución.Consideremos:

template <int n> 
struct Foo 
{ 
    //something dependent on n 
}; 

y

template <typename T> 
struct Bar; 

template <int n> 
struct Bar <val <int, n> > 
{ 
    //something dependent of n 
}; 

Foo y el bar son funcionalmente equivalentes, todas las plantillas meta-Kadabra que se puede hacer con Foo también se puede hacer con Bar (sólo tiene que pasar Val en lugar de n). Lo mismo se puede embalar vay puntero a miembro en val <>:

val <typeof (&My::a), &My::a> 

estos valores de tiempo de compilación ahora pueden ser almacenados en listas de tipos (como el impulso :: :: MPL algo), en comparación, transformado, etc., todo compila tiempo Y cuando usted finalmente desee utilizarlos como puntero a miembro en tiempo de ejecución, simplemente definir la plantilla una función:

template <typename T, T value> 
T 
extract (val <T, value>) 
{ 
    return value; 
} 

y utilizarla:

typedef val <typeof (A::i), A::i> a_i; 

A a; 
std::cout << (a .* extract (a_i())); 

PD: hay algunas construcciones torpes acerca de esta solución, pero es todo por simplicidad y explicación. Por ejemplo bastante feo (a * extracto (a_i()).) Puede simplificarse envolviéndolo en algo más puntero a miembro específico:

template <typename M, typename C> 
typename mem_type <M>::value & 
mem_apply (C &c) 
{ 
    M m; 
    return c .* extract (m); 
} 

donde mem_type es plantilla de clase que extrae tipo de miembro referido por M. a continuación, el uso sería:

std::cout << mem_apply <a_i> (a); 
Cuestiones relacionadas