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

C++ 将方法指针作为模板参数传递:为什么';我不能在这里使用继承的方法吗?,c++,templates,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

下面的代码(从一个较大的程序压缩而来)不使用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();
}
struct S1{
void m1(){}
};
模板结构S2:B{
void m2(){}
空隙m3();
};
模板无效f1(S*o){
(o->*m)();
}
模板void S2::m3(){
f1(本);
}
int main(){
void(S2::*m)(=&S2::m1;
s2o;
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.
错误。cc:15:3:错误:调用“f1”时没有匹配的函数
f1(本);
^~~~~~~~~~~~~~~
错误。cc:21:5:注意:在成员函数“S2::m3”的实例化中请求了
在这里
o、 m3();
^
错误。cc:10:43:注意:忽略候选模板:显式指定无效
模板参数“m”的参数
模板无效f1(S*o){
^
生成1个错误。

当我用
m2
替换
m1
时,这段代码会编译。显然,编译器知道
m1
(当我用
m4
替换
m1
时,会有不同的消息),那么为什么指向它的指针在这种上下文中是无效的呢?

问题是,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();
}
struct S1{
void m1(){}
};
模板结构S2:B{
void m2(){}
空隙m3();
};
模板无效f1(S*o){
(o->*m)();
}
模板void S2::m3(){
f1(本);
}
int main(){
s2o;
o、 m3();
}
当然,这(还)不能扩展到间接基类中定义的方法,但通过一点TMP,它是可以做到的(看看我能否在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);
}
模板无效f1(B*o,MF mfp){
(o->*mfp)();
}
模板void S2::m3(){
f1(这个和B::m1);
}

您可以/应该使用typetraits来确保
S2&
可以转换为a
B&
,如果类布局没有明确保证这一点,就像您当前的示例一样。

为什么不说
f1(这个);
?@Kerrek SB因为
S1
不是
B
的唯一可能。它确实与
B
一起工作,而不是
S2
,这是很有用的,但是如果我不必假设
m1
是在基类中定义的,那就太好了。@notagain请看我更新的回答
m1
的类型是
void(S1:*)(无效)<代码> S2< /代码>继承了该方法。确实,它继承了<代码> S1::M1< /代码>,而不是<代码> S2::M1<代码>,因为继承<代码> S1::M1没有神奇地定义一个新函数<代码> S2::M1。考虑链接器在被请求地址时所寻找的内容?也许您与虚拟函数混淆了。ons?让我细化一下:
m1
的类型是
void(S2::*)()
的一个子类型。为什么会有错误?另外,如果
S2
要成倍继承
S1
之前存储的一些字段,我希望编译器可以使用一种可能的实现策略来创建一个新地址()@notagain:re第二个:指向成员函数的指针不是指针,它们肯定不是地址。(可能在封面下,它们被实现为类型标识+地址或vtable中的偏移量;)。要点,
m1是void的子类型(S2::*)()
是错误的声明