Pergunta

Considere o seguinte código

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
};

Olha, é possível salvar qualquer parâmetro do modelo se este parâmetro é um typename ou um valor inteiro. A coisa é que ponteiro para membro é um deslocamento, ou seja inteiro. Agora eu quero salvar qualquer ponteiro para membro em tempo de compilação:

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));
}

Como salvar ponteiro ao membro em tempo de compilação ? Eu uso VS2008.

Nota. tempo de compilação é crítica. Por favor, não escreva solução em tempo de execução. Eu sei disso.

Foi útil?

Solução

Por meio de um modelo?

#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;
}

Os seguintes compila mp a esta on gcc-4.4.1 (eu não tenho acesso a MSVC no momento):

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

É apenas um deslocamento para o membro, o que parece muito tempo de compilação para mim.

Com modelo, você precisa especificar o exterior definição da classe:

#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));
}

O que compila para:

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

No entanto, este todos cheira a biblioteca padrão apresenta reimplementação (veja std::tr1::mem_fn).

Outras dicas

Seria bom ter explicação mais elaborada do porquê 'em tempo de compilação é importante' (ajuda sugerindo alternativas). Mas, para minha noção de tudo o que você necessidade para ser feito em tempo de compilação com ponteiro-para-membro, na verdade você pode fazer. Meu variante é a sugestão de Thomas misturado com alguma filosofia C ++ da espécie. Primeiro vamos definir:

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

este template struct pode efetivamente servir como valor de tempo de compilação, e você não precisa de "valor estático = v;", para usá-lo tanto na compilação ou tempo de execução. Considere o seguinte:

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

e

template <typename T>
struct Bar;

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

Foo e Bar são funcionalmente equivalentes, cada modelo de meta-kadabra que pode ser feito com Foo também pode ser feito com Bar (apenas passar val vez de n). O mesmo vay você pode embalar ponteiro ao membro em val <>:

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

Estes valores de tempo de compilação agora podem ser armazenadas na lista de tipo (como boost :: mpl :: algo), em comparação, transformada, etc., tempo tudo compilação. E quando você finalmente vai querer usá-los como ponteiro-para-membro em tempo de execução, apenas definir um modelo de função:

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

e usá-lo:

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

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

P.S .: existem algumas construções desajeitados sobre esta solução, mas é tudo por causa da simplicidade e explicação. Por exemplo, em vez feio (a * extracto (a_i ()).) Pode ser simplificada por envolvê-lo em algo mais específico ponteiro-para-membro:

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

onde é mem_type modelo de classe que extrai tipo de membro referido por M. Em seguida, o uso seria:

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

Você não pode.

Mas você pode usar um functionoid em seu lugar. Este pode ser uma solução em tempo de compilação. E porque o compilador pode in-line coisas, é possivelmente ainda mais rápido do que um ponteiro para uma função membro. Exemplo:

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);
}

Não testado, mas você começa a idéia.

Você não pode inicializar um membro estático dentro da definição de um struct. Ele precisa ser declarada fora como este (que provavelmente não é o que você pretendia com o modelo, mas de qualquer maneira):

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));
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top