2013-12-09 46 views
4

長的解釋如下,問題在底部。C++自動實現函數和ODR

我的問題具體是指當前的C++草案標準(也是當前的主要標準),找到here。更具體地說,關於成員職能和ODR,第3.2節第6點(第35頁)陳述了each definition of D shall consist of the same sequence of tokens

我最近在添加新數據分析時在項目中遇到了以下問題。

我正在寫一個文件A.cpp。我創建了一個小的虛擬結構來存放一些數據。在這個例子中,我將其稱爲Data

namespace Example { 
struct Data { 
    //etc 
}; 
//Use Data 
}; 

然而,在花葯文件,B.cpp,已經有一個結構稱爲DataExample命名空間中。編譯器爲這兩個類生成Data::~Data();,然後調用它們各自成員的析構函數。 B.cpp中的定義包含一個向量,當使用A.cpp中定義的佈局調用Data結構時,該向量在被破壞時引起爆炸。雖然這兩個結構似乎都能正常工作,沒有編譯時錯誤,但鏈接時鏈接器會選擇一個定義並使用它,而忽略其他定義。 (因此在A.cpp內的Data物體上引起爆炸)

在GCC或MSVC下不會發出警告。 當啓用優化時,問題不會發生(函數被內聯,沒有鏈接時間混淆)。

我的問題是,該標準只規定行爲未定義If D is a template and is defined in more than one translation unit。 要麼我誤解了標準,而是讓未定義的行爲默默地發生;或者GCC和MSVC都默默地接受他們不應該的東西(並且應該拒絕產生輸出或發出警告)(目前的情況是未定義的,而且沒有診斷的行爲不一致)。

是否有人可以幫助我理解這與在類中未定義的函數的衝突定義(這會導致警告/錯誤)的區別。

+2

這是一個ODR違規行爲,但這種違規行爲是「格式不對,不需要診斷」。編譯器不必告訴你。 – Simple

+0

謝謝,但似乎在3.2點4定義。點6似乎處理成員函數,並沒有說明它是未定義的行爲。 – James

+2

好吧,它是未定義的而不是IFNDR(雖然兩者都是真實的意思)。編譯器生成的成員函數隱式地是'inline',這意味着這兩個函數違反了ODR,因爲您在問題開始時引發了這樣的問題:* D的每個定義都應該包含相同的標記序列*。 – Simple

回答

3

有人可以幫我嗎瞭解這與在類中未定義的函數的衝突定義(這會導致警告/錯誤)的不同之處。

的區別是一個類內部的函數定義隱含名義上直列,這要是功能的再次遇到抑制編譯器警告。這並不意味着編譯器必須將它們內聯 - 它可能會決定使用任何啓發式函數而不打擾,或者可能很簡單,從不在某些優化級別內聯。無論如何,如果你鏈接的代碼看到名義上的內聯非成員函數的不同定義,你就會遇到完全相同的問題。

請參閱3。2/6

可以有類類型的多個定義...在程序中提供的每個定義出現在一個不同的翻譯單元,以及所提供的定義滿足下列要求。

- D的每個定義應由相同的令牌序列組成;和

[其他要求]

更一般地,你應該把你的代碼匿名命名空間 ...它們被設計用來防止這種跨翻譯單位問題。

+0

3.2/4似乎處理非內聯函數,並且僅聲明內聯函數必須在使用它們的每個翻譯單元中定義。在這種情況下,每個功能在使用翻譯單元的地方都有一個單一的定義。 – James

+0

詹姆斯:你是對的 - 根據上面的編輯,相關部分的3.2/6。 –

+2

隱式定義的函數是否由令牌組成? (我認爲這是UB,因爲ODR也適用於類;但我不確定它是否適用於隱式定義的函數。) – dyp

0

什麼是多重定義並不重要,這是不確定的行爲。未定義的行爲會導致任何事情 - 包括編譯器或運行時沒有診斷的工作程序。以兩種不同的方式定義相同的名稱不是格式良好的代碼,無論它是否是成員函數,自由函數,結構或類,全局變量等。

+0

爲了澄清,你能否指出標準中的哪些地方屬於未定義的行爲?我包括了我認爲是C++標準的相關部分,但似乎並未涵蓋這種情況。 – James