Como salvar ponteiro ao membro em tempo de compilação?
-
19-09-2019 - |
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.
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));
}