6

考虑以下代码

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

4 回答 4

4

最好能更详细地解释为什么“编译时间很重要”(有助于提出替代方案)。但在我看来,您需要在编译时使用指向成员的指针完成所有工作,您实际上可以做到。我的变体是 Thomas 的建议与某种 C++ 哲学相结合。首先让我们定义:

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

此结构模板可以有效地用作编译时值,并且您不需要“静态值 = 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 完成的模板 meta-kadabra 也可以用 Bar 完成(只需传递 val 而不是 n)。同样,您可以将指向成员的指针打包到 val<> 中:

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

这些编译时间值现在可以存储在类型列表中(如 boost::mpl::something)、比较、转换等,一切都是编译时间。当您最终想在运行时将它们用作指向成员的指针时,只需定义一个函数模板:

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

PS:关于这个解决方案有一些笨拙的结构,但这都是为了简单和解释。例如,相当丑陋的(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);
于 2011-03-12T09:36:56.570 回答
1

你不能。

但是您可以改用functionoid。这可以是一个编译时解决方案。而且因为编译器可以内联,它可能比指向成员函数的指针还要快。例子:

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

未经测试,但你明白了。

于 2009-12-16T09:57:59.650 回答
1

为什么要使用模板?

#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)。

于 2009-12-16T10:57:39.610 回答
0

您不能在结构的定义中初始化静态成员。它需要像这样在外面声明(这可能不是你想要的模板,但无论如何):

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));
}
于 2009-12-16T10:35:45.987 回答