簡體   English   中英

在模板基類中的虛函數中使用前向聲明的類,其中構造函數只需要前向聲明?

[英]Use a forward-declared class in a virtual function in a template baseclass where the constructor only needs the forward declare?

我試圖找出失敗的原因。 我正在使用的代碼基本上壓縮到下面的代碼。 我有一個簡單的A類,我專門用一個模板。 模板不需要這種類型來編譯它的構造函數,並且我實際調用的構造函數(派生類型)沒有公開,因此編譯器此時無法為構造函數生成代碼。

GCC和Clang沒有。 然而,MSVC(2008 + 2010)確實嘗試編譯虛擬成員,因此不進行編譯。

這是錯誤的GCC和Clang,還是來自MSVC? 或者我是否會進入UB領土?

class A;

template <typename X>
class S {
public:
    S() {}

    virtual int useX() { return X::value; }
};

class T : public S<A> {
public:
    T();
};

int main()
{
    new T();
    return 0;
}

當MSVC實例化一個類時,它還會填充其vtable,並為此目的實例化其所有虛函數,即使是那些從未調用過的函數。

在您的情況下,如果沒有編譯器看到A的完整定義,則無法實例化函數useX

如果您將useX聲明為非虛擬,則MSVC可以正常工作。

看來這種行為依賴於編譯器; 例如,AIX在實例化(未使用)函數時比MSVC更具攻擊性。

首先,正如許多人所說,標准完全允許編譯器在這種情況下實例化(或選擇不實例化)他們的內容; 通過嚴格解釋標准,這是代碼問題,而不是MSVC錯誤。 您依賴的行為是特定於平台的,因此是非標准的; 雖然這不是一個“蟲子”本身,但它顯然是不便攜的。

但是,理解為什么GCC和Clang與MSVC有所不同,這與在每個編譯器中如何設置vtable有關。

快速概述對象在內存中的外觀:

  1. 沒有繼承,所有調用都是靜態的,沒有vtable或調整
  2. 單繼承,所有調用都是靜態的,聰明的組織
  3. 多重繼承,所有調用都是靜態的,指針調整
  4. 虛擬繼承,調用可以是動態的,vtable和指針調整

在第一種情況下,所有信息都可以是靜態的; 成員函數只是隱藏的正常函數。 在第二種情況下,派生類(或類,對於長繼承列表)可以巧妙地放在內存中的基類之后,以便Derived * == Base *。 顯然,這種優化不適用於多重繼承,這意味着每次調用都需要調整this指針。 然后,虛擬繼承添加一個數組vtable,它在運行時動態選擇正確的函數。

這與未使用的成員的實例化有什么關系?

一般而言,無論您的實施方式如何,您都需要三件事:

  1. 三角形,詳細說明您所關心的子對象在哪里
  2. 虛擬索引(vindex),詳細說明要調用的虛擬方法
  3. 一組要調用的方法(vtable)

G ++有一個標准化且復制良好的方式來進行這些調整,在許多編譯器中使用(包括Clang,因為Clang意味着要取代GCC):

  1. 明確存儲增量
  2. 在vtable中也存儲vindex和偏移量計算,方法地址在evens和vindex上的賠率

這是一種奇怪的緩存優化,它有一些好處; 它被廣泛復制,盡管不是最優雅的解決方案。 它對所有情況使用一種結構,並且每次執行相同的計算,具體取決於編譯器優化以在適用時用直接調用替換虛擬表跳(GCC優化器非常出色的任務)。

另一方面,MSVC不僅有點不優雅。 這是一個可怕的黑客:它為每個案例使用不同的結構。 這允許它避免不必要的計算的開銷,並且在許多情況下節省空間。 但是,這意味着轉換成員函數指針會導致它們更改大小,在常見情況下(派生到基礎)會導致它們丟失信息

因此,與更優雅的GCC實現不同,MSVC絕對必須實例化虛擬成員函數; 如果沒有,它很容易在翻譯單元之間或鏈接過程中丟失信息,並且具有由不同大小的結構表示的相同對象!

這是一個問題,他們實際上添加了關鍵字來處理它: http//msdn.microsoft.com/en-us/library/ck561bfk.aspx

所以,雖然你不認為編譯器需要這種類型,但MSVC肯定會這樣做,否則它幾乎肯定是不安全的。

編輯:混淆自己簡單的單繼承,基類去了第一:

class A : public B

[[B] A]

所以指針* B == * A.

從草案C ++ 1y標准:(通過@DyP)

實現不應隱式實例化函數模板,變量模板,成員模板,非虛擬成員函數,成員類或不需要實例化的類模板的靜態數據成員。 如果虛擬成員函數不會被實現,則實現是否隱式實例化類模板的虛擬成員函數是未指定的。 在默認參數中使用模板特化不應導致模板被隱式實例化,除了可以實例化類模板,其中需要其完整類型來確定默認參數的正確性。 在函數調用中使用默認參數會導致默認參數中的特化被隱式實例化。

強調我的。 任何C ++編譯器都可以在任何時候實例化template任何virtual函數,而不會違反C ++標准。 因此,MSVC是符合此事項的標准。

這是目前的N3797草案中的14.7.1.11

暫無
暫無

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

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