簡體   English   中英

從派生實例的基類中調用派生類中的內聯成員

[英]calling inline member in derived class from base class of derived instance

我有兩個類,我們稱它們為A和B,看起來像這樣:

class A {
public:
    inline void f1(int n) { f2(n&1); }
    inline void f2(int n) { } // no-op in base class
};

class B : public A {
public:
    inline void f2(int n) { printf("n=%d\n",n); } // printf for debugging only
};

現在我實例化B,然后調用f1。

int main() {
    B myB;

    myB.f1(500);
}

我期望發生的事情是,它將在B中調用f1(),它繼承自A,這將轉而使用LSB 500,並將其傳遞給B中的f2(),然后將其打印出來。 相反,發生的是它在A中調用f1()。

為什么這不起作用? 編譯器在編譯時就知道myB是B類,而不是A類。出於以下兩個原因,我無法使f1或f2是虛擬的:首先,由於性能原因,我不能忍受任何函數調用開銷;其次,使它與某些常量參數內聯可以使編譯器經常優化很多代碼。 我曾嘗試使f1()在A中成為內聯虛擬函數,但即使使用-O3我仍然看到(在一個特定的測試中,與本示例無關的更多代碼)約500字節的匯編代碼,而我只是8字節執行以下等效操作:

class A {
public:
    inline void f1(int n) { f2(n&1); }
    inline void f2(int n) { } // no-op in base class
};

class B : public A {
public:
    inline void f1(int n) { f2(n&1); }
    inline void f2(int n) { printf("n=%d\n",n); } // printf for debugging only
};

現在很明顯,我可以將A中的f1復制/粘貼到所有派生類,但是在實際代碼中,最終將是大約10個類,每個函數大約有35個而不是1個,並且保持#include相同似乎是很愚蠢的做法一遍又一遍地編寫代碼,以避免在很多地方復制此代碼所帶來的難以置信的維護問題。

這里有兩個問題:

  1. 在A的上下文中調用f2()實際上會調用A::f2() -句點。 如果您想要多態行為,則必須將f2虛擬化。

  2. 您已經假定將f2虛擬化會增加運行時的開銷。 實際上,如果編譯器可以證明您正在B上調用f1() ,則不會有任何開銷。

證明在這里: https : //godbolt.org/g/2OWPo2

另外,不需要內置關鍵字。 如果在聲明時定義方法的主體,則該方法是隱式內聯的。

此外(與直覺相反), inline不會導致生成內聯代碼。 它只是警告編譯器它可能會多次看到該定義。

內聯是編譯器將自己進行的優化。

最后,再次查看生成的程序集。 您將看到編譯器甚至沒有費心發出vtable或任何vtable構造。 這幾天的優化程序真的很好

實際上,您買不起虛擬功能是很常見的。 在這種情況下,您需要重新設計:

template<typename Impl>
class ABase {
public:
    inline void f1(int n) { f2(n&1); }
    inline void f2(int n) { static_cast<Impl*>(this)->f2(n); }
};

class AReal : public ABase<AReal> {
    inline void f2(int n) { } // no-op here
};

class B : public ABase<B> {
public:
    inline void f2(int n) { printf("n=%d\n",n); } // printf for debugging only
};

注意,您仍然不能指望動態多態性,並且AReal和B現在不再相關。 (如果不需要后者,可以通過在ABase中添加非模板庫來解決;如果沒有與虛擬庫相當的開銷,則不能解決前者。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM