Как сохранить указатель на член во время компиляции? - PullRequest
6 голосов
/ 16 декабря 2009

Рассмотрим следующий код

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

Посмотрите, можно сохранить любой параметр шаблона, если этот параметр является именем типа или целочисленным значением. Дело в том, что указатель на член является смещением, то есть целым числом. Теперь я хочу сохранить любой указатель на член во время компиляции:

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

Как сохранить указатель на член в время компиляции ? Я использую VS2008.

Примечание. Время компиляции имеет решающее значение. Пожалуйста, не пишите решение во время выполнения. Я это знаю.

Ответы [ 4 ]

2 голосов
/ 12 марта 2011

Было бы неплохо получить более подробное объяснение того, почему «время компиляции важно» (помогает предлагать альтернативы). Но, на мой взгляд, все, что вам нужно для выполнения компиляции с указателем на член, на самом деле может сделать. Мой вариант - предложение Томаса, смешанное с некоторой философией C ++. Сначала давайте определим:

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

этот шаблон структуры может эффективно служить значением времени компиляции, и вам не нужно "static value = v;", чтобы использовать его либо во время компиляции, либо во время выполнения. Рассмотрим:

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

и

template <typename T>
struct Bar;

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

Foo и Bar функционально эквивалентны, каждый шаблон мета-кадабры, который может быть выполнен с помощью Foo, также может быть выполнен с помощью Bar (просто передайте val вместо n). То же самое, что вы можете упаковать указатель на член в val <>:

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

эти значения времени компиляции теперь могут храниться в списках типов (например, boost :: mpl :: кое-что), сравниваться, преобразовываться и т. Д. Во все время компиляции. И когда вы, наконец, захотите использовать их как указатель на член во время выполнения, просто определите один шаблон функции:

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

и используйте его:

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

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

P.S .: Есть некоторые неуклюжие конструкции об этом решении, но все это ради простоты и объяснения. Например, довольно некрасиво (a. * Extract (a_i ())) можно упростить, обернув его во что-то более специфичное для указателя на член:

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

где mem_type - шаблон класса, который извлекает тип члена, на который ссылается M. Тогда использование будет:

std::cout << mem_apply <a_i> (a);
1 голос
/ 16 декабря 2009

Зачем использовать шаблон?

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

Следующие компиляции mp для этого на gcc-4.4.1 (в данный момент у меня нет доступа к MSVC):

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

Это просто смещение для члена, что для меня выглядит довольно компиляцией.

С шаблоном вам нужно указать определение вне класса:

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

Который компилируется в:

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

Однако, все это попахивает функциями стандартной библиотеки для переопределения (см. std::tr1::mem_fn).

1 голос
/ 16 декабря 2009

Вы не можете.

Но вместо этого вы можете использовать функционоид . Это может быть решением во время компиляции. И поскольку компилятор может встроить что-то, возможно, он даже быстрее, чем указатель на функцию-член. Пример:

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

Не проверено, но вы поняли.

0 голосов
/ 16 декабря 2009

Вы не можете инициализировать статический член внутри определения структуры. Это должно быть объявлено снаружи, как это (что, вероятно, не то, что вы хотели с шаблоном, но в любом случае):

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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...