将方法指针作为模板参数传递:为什么我不能在这里使用继承方法?

时间:2012-02-03 20:15:48

标签: c++ templates

以下代码(从较大的程序压缩而来)不能用clang或gcc编译。

struct S1 {
  void m1() {}
};

template<typename B> struct S2 : B {
  void m2() {}
  void m3();
};

template<typename S, void (S::*m)()> void f1(S* o) {
  (o->*m)();
}

template<typename B> void S2<B>::m3() {
  f1<S2, &S2::m1>(this);
}

int main() {
  void (S2<S1>::*m)() = &S2<S1>::m1;
  S2<S1> o;
  o.m3();
}

以下是clang的错误消息:

bad.cc:15:3: error: no matching function for call to 'f1'
  f1<S2, &S2::m1>(this);
  ^~~~~~~~~~~~~~~
bad.cc:21:5: note: in instantiation of member function 'S2<S1>::m3' requested
      here
  o.m3();
    ^
bad.cc:10:43: note: candidate template ignored: invalid explicitly-specified
      argument for template parameter 'm'
template<typename S, void (S::*m)()> void f1(S* o) {
                                          ^
1 error generated.

当我将m1替换为m2时,此代码会进行编译。很明显,编译器知道m1(我用m1替换m4时的不同消息),那么为什么在这种情况下指向它的指针是无效的呢?

1 个答案:

答案 0 :(得分:3)

问题是,m1的类型是void(S1::*)(void),而不是void(S2::*)(void)。因此,通过利用已知的基类名称来修复它:

struct S1 {
    void m1() {}
};

template<typename B> struct S2 : B {
    void m2() {}
    void m3();
};

template<typename S, typename B, void (B::*m)(void)> void f1(S* o) {
    (o->*m)();
}

template<typename B> void S2<B>::m3() {
    f1<S2, B, &B::m1>(this);
}

int main() {
    S2<S1> o;
    o.m3();
}

当然这还没有(还)扩展到间接基类中定义的方法,但是有一点TMP就可以完成(看看我是否可以在Going Native 2012的中场休息期间发布:))

更“灵活”的方法是:

template<typename B, typename MF> void f1(B* o, MF mfp) {
    (o->*mfp)();
}

template<typename B> void S2<B>::m3() {
    f1(this, &B::m1);
}

如果类布局尚未明确保证,{/ 1>}可以/应该使用typetraits确保S2<B>&可转换为B&