以下代码(从较大的程序压缩而来)不能用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
时的不同消息),那么为什么在这种情况下指向它的指针是无效的呢?
答案 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&
。