2012-02-03 3 views
3

次のコード(より大きいプログラムから圧縮されている)は、clangまたはgccでコンパイルされません。メソッドポインタをテンプレートパラメータとして渡します:ここで継承されたメソッドを使用できないのはなぜですか?

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. 

私はm2m1を交換するときに、このコードはコンパイルされます。

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

はここで打ち鳴らすのエラーメッセージです。明らかにコンパイラはm1m1m4に置き換えたときに別のメッセージを知っている)ので、なぜこのコンテキストではポインタが無効であるべきですか?

+0

「f1 (this);」と言ってみませんか? –

+0

@Kerrek SB 'S1'は' B'の唯一の可能性ではないので。 – notagain

+0

'S2'ではなく' B'で動作しますが、これは知っておくと便利ですが、 'm1'が基本クラスで定義されていると仮定する必要がない場合はいいでしょう。 – notagain

答えて

3

事実、m1のタイプはvoid(S1::*)(void)であり、void(S2::*)(void)ではありません。この(まだ)しかし、それを行うことができるTMPのビットと、間接的な基底クラスで定義されたメソッドに拡張しませんもちろん

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

(かどうかを確認します:だから知らベースクラス名を活用して、それを修正あなたが/ S2<B>&B&に変換されることを保証するためにtypetraitsを使用する必要がある可能性があり

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

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

:私はネイティブ2012行くの休憩が続く間:))

をより「柔軟な」アプローチが可能だろうと投稿することができますクラスレイアウトがまだない場合現在の例のように明示的に保証します。

+0

'm1'の_typeは' void(S1 :: *)(void) '_です。 'S2 'はそのメソッドを継承します。 – notagain

+0

本当に!後者は存在しないので、 'S1 :: m1'は' S2 :: m1'ではなく継承します。 'S1 :: m1'を継承すると、新しい関数' S2 :: m1'が魔法のように定義されません。住所を尋ねられたときにリンカーが探しているものを考慮しますか?おそらくあなたは仮想関数と混同されているでしょうか? – sehe

+0

私はそれを改良しましょう: 'm1'の型は' void(S2 :: *)() 'のサブタイプです。エラーが発生するのはなぜですか? – notagain

関連する問題