2016-07-28 164 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++中的內容即可。如果你只是想告訴編譯器在'base'中存在'derived :: foo',你可以直接寫'&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)(); 

實際上將調用derived::foo,完全像一個簡單的通話pBase->foo()會。

0

即使有保證,它可能會使用僅在derived中聲明的其他數據成員。一個可能的解決方案是使用函數指針並通過this作爲第一個參數(這也顯示了爲什麼你不能通過virtual)。

+0

由於我傳遞'base *',因爲運行時多態性,最終會導致正確的重寫,所以我無法訪問任何不存在的東西。或者我誤解了你? –

+0

添加一個'int i;'來派生並做'i = 0xDEADBEEF;'到'derived :: foo()'來看看我的意思。它反過來工作:你可能會傳遞'Base'函數指針,其中'derived'是所期望的。注意,*你知道你正在傳遞'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中的哪些信息,因爲這些信息已被丟棄。爲了避免這種模棱兩可的情況,不允許使用upcast。

+0

我想我的失敗點在於vtable查找也發生在函數指針上(並且在那種情況下,實際上會調用正確的方法),但我想它不會發生。 –

+0

「_哪個函數應該被調用?_」'foo()',我猜想 – curiousguy