2016-07-28 11 views
-1

私は、単に派生クラスメンバ関数ポインタを、基本クラスメンバ関数ポインタにキャストできない理由を理解しています(here)。純粋な仮想メンバをベースにしたキャスト派生仮想オーバーライド

しかし、このスニペット与えられた:私がお聞きしたかった

struct base 
{ 
    virtual void foo() = 0; 
}; 

struct derived : base 
{ 
    void foo() override {}; 
}; 

struct invoker 
{ 
    typedef void(base::*target)(); 

    invoker(base* b, target t) 
    { 
     (b->*t)(); 
    } 
}; 

template<typename B, typename D> 
void (B::*cast(void (D::*method)()))() 
{ 
    return static_cast<void(B::*)()>(method); 
} 

derived d; 
invoker bad(&d, &derived::foo); //C2664 
invoker good(&d, cast<base>(&derived::foo)); 

は、コンパイラが、それは純粋仮想メソッドで理解し、そしてそれが階層間でどこかに実装されますので、基本関数のシグネチャを飾るすることが可能です(そうでなければ、タイプBのオブジェクトを構築できませんでした)?私はなぜこれを通常の関数で行うことができないのか理解していますが、純粋な仮想関数の場合にはコンパイラには実装が保証されています。(完了していない場合はクラスBに関するエラーが発生します。キャスト)。

+1

あなたが求めている装飾の種類は不明です。あなたがC++デザイン委員会の最高責任者だったら、どうしますか?一貫性や正確性を心配する必要はありません.C++に追加したいものだけを表示してください。 'derived :: foo'が' base'に存在することをコンパイラに伝えたいのであれば、単に '&base :: foo'と書くことができます。 –

+0

@ n.m。私は明示的なキャストを避けたい:Dしかし、それは "私が認識していない何らかの方法がある"という質問のタイプであった。 –

+1

いいえ、キャストしないでください。 '&base :: foo'から始めましょう。 '&derived :: foo'は'&base :: foo'でできないものは何ですか? –

答えて

2

&derived::fooのタイプを操作する必要はありません。代わりに&base::fooを使用することができます。

メンバー関数へのポインタは仮想性を尊重します。この呼び出し

base* pBase = new derived; 
auto pFoo = &base::foo; 
(pBase->*pFoo)(); 

は実際には、正確に、簡単なコールpBase->foo()だろうように、derived::fooを呼び出します。

0

実装されていることが保証されていても、derivedでのみ宣言された追加データメンバーを使用することがあります。考えられる解決策は、関数ポインタを使用し、最初のパラメータとしてthisを渡すことです(これはまた、virtualで実行できない理由を示しています)。

+0

私は 'base *'を渡しているので、実行時のポリモーフィズムのために正しいオーバーライドで終わるので、そこにないものにはアクセスできません。それとも私はあなたを誤解しましたか? –

+0

'int i;'を導出に追加し、 'i = 0xDEADBEEF;'を 'derived :: foo()'に追加して、私が意味するものを見てください。これは逆の動作です: '派生'が期待される場所に 'Base'関数ポインタを渡すかもしれません。あなたは 'derived *'を渡していることを知っていますが、C++コンパイラはそれを知りません。 – lorro

+0

いいえ、純粋な仮想メンバを持っているので、 'Base'のインスタンスを構築できないのでできません。私は一般的な事例について話しているのではなく、キャストのターゲットが「ベース」の純粋な仮想メンバーであるところについて話しています。 –

0

には、以下のダイヤモンドの階層を考えてみましょう:

struct base { 
virtual void foo() = 0; 
}; 

struct D1 : public virtual base { 
virtual void foo() override; 
}; 

struct D2 : public virtual base { 
virtual void foo() override; 
}; 

struct Derived : public virtual D1, D2 { 
virtual void foo() final; 
}; 

今*ベースにする:: * ::アップキャストが派生から許可されているシナリオを検討してください。どの関数を呼び出さなければならないか?コンパイラは、情報がキャストされてから呼び出すD1 :: foo、D2 :: foo、またはDerived :: fooのいずれかに関する情報を失います。この種のあいまいさを避けるために、そのようなアップキャストは禁止されています。

+0

私の失敗のポイントは、vtableルックアップは関数ポインタでも起こるということでした(その場合、実際には正しいメソッドが呼び出されます)が、それは起こらないと思います。 –

+0

"_どの関数を呼び出すべきですか?_" 'foo()'、私は推測します – curiousguy

関連する問題