2011-03-04 122 views
7

我知道爲什麼包括警衛存在,而且#pragma once不標準,因此並不是所有的編譯器等支持有什麼情況下你不想包括警衛?

我的問題是一種不同的:

是否有任何合理的理由永遠沒有他們?我還沒有遇到這樣一種情況,理論上說,如果不在某個文件中提供包括守衛在內的任何其他地方,那麼將會有任何好處。有沒有人有一個沒有他們的實際好處的例子?

我問的原因 - 對我來說,他們看起來相當多餘,因爲你總是使用它們,並且#pragma once的行爲也可以自動應用於字面上的一切。

回答

8

我已經看到了根據包含之前定義的宏生成代碼的頭文件。在這種情況下,有時需要將這些宏定義爲一個(一組)值,包括頭,重新定義宏並再次包含。每個看到這種情況的人都認爲它很醜並且最好避免,但有時(比如說,如果標題中的代碼是通過其他方式生成的),那麼這樣做更不容易。

除此之外,我想不出一個理由。

+0

我遇到的FFTW庫這個特定用途(這是前一段時間,也許它現在改)。定義了許多函數,以便可以爲不同的基礎類型創建它們:in,double,float等,因此您可以根據需要定義多種類型,並重新包含該文件。但是,這是一個C庫。在C++中,我們將使用模板... –

+0

@the_mandrill:在C++中,這實際上用於模板不適合問題的某些情況。特別是使用boost預處理器庫自動生成具有不同數量參數的相同代碼,如帶有0,1,2 ... N參數的boost :: bind。 –

+0

@DavidRodríguez:據我所知,boost :: bind是通過爲最多9個參數提供適當的模板來實現的。編輯:好吧我找到了一個地方,在這種情況下,避免使用模板參數作爲返回值(當返回類型爲void時)的函數return x'的情況,但我一直設法通過特定的模板特化解決這些問題對於'void'類型。他們走的路線沒有包括警衛,但是爲了解決這個問題,重新定義「return」宏並不是必要的。 – Mephane

0

如果項目中有兩個標題使用相同的包含保護,例如,可能會出現問題。如果您有兩個第三方庫,並且它們都有一個使用包含防護符號(例如__CONSTANTS_H__)的標頭,則在給定的編譯單元中,兩個標頭都不能成功使用#include。更好的解決方案是#pragma once,但一些較老的編譯器不支持這一點。

+2

更重要的是,'#pragma once' ___不是標準_,並且不能保證你的編譯器的下一個版本仍然支持它,或者支持相同的語義。對我而言,這是一個不使用它的好理由。 – sbi

+1

@sbi:true - 正如往常一樣,這是在可移植性和諸如上面描述的問題之間的折衷。我想你總是可以嘲諷一些非常醜陋的樣板文件,從而測試編譯器和編譯器版本,然後相應地使用包括guard或'#pragma once',但我不確定我希望在每個頭文件中都能看到它。 –

+0

好吧,如果兩個包括警衛碰撞,答案是改變其中之一,不要忽略... – Mephane

0

假設您有第三方庫,並且無法修改其代碼。現在假設包含來自該庫的文件會生成編譯器警告。您通常希望以高警告級別編譯自己的代碼,但這樣做會導致使用該庫的大量警告。您可以編寫警告disabler/enabler頭文件,然後您可以將其包裝在第三方庫中,並且應該可以多次包含它們。

另一個更復雜的種類使用的是Boost的預處理器的迭代構建體: http://www.boost.org/doc/libs/1_46_0/libs/preprocessor/doc/index.html

+0

我不明白怎麼沒有包裝衛士包裝會有所作爲。如果你已經包含了包裝器,那麼* *包含的第三方頭文件的警告被禁用,並且第二次包含包裝器時,它將被包含守衛抓住,而不是任何東西。 – Mephane

+0

@Mephane:這是包含特定於編譯器的警告控件編譯指示的標題,它們沒有包含警衛,也沒有包含警告的庫標題。 –

3
<cassert> 
<assert.h> 

「斷言宏根據NDEBUG的那 < ASSERT.H>包括在每個時間的當前狀態重新定義。 「

4

@sbi已經討論過代碼生成,所以讓我舉個例子。

說你有很多項目的枚舉,和你想生成一組函數用於它的每個元素...

一種解決方法是使用多包容伎倆。

// myenumeration.td 
MY_ENUMERATION_META_FUNCTION(Item1) 
MY_ENUMERATION_META_FUNCTION(Item2) 
MY_ENUMERATION_META_FUNCTION(Item3) 
MY_ENUMERATION_META_FUNCTION(Item4) 
MY_ENUMERATION_META_FUNCTION(Item5) 

那麼,人們只是用它像這樣:

#define MY_ENUMERATION_META_FUNCTION(Item_) \ 
    case Item_: return #Item_; 

char const* print(MyEnum i) 
{ 
    switch(i) { 
    #include "myenumeration.td" 
    } 

    __unreachable__("print"); 
    return 0; // to shut up gcc 
} 

#undef MY_ENUMERATION_META_FUNCTION 

這是否是好的或hackish的是你的,但很顯然它是有用的,有通過一切工具的功能,每次抓取一個新的值被添加到枚舉中。

+0

如果您將'#define MY_ENUMERATION_META_FUNCTION'放在頂端myenumeration.td中,則可以只包含第二個文件*,並且可以包含第一個文件一次。你的例子看起來像一個任意的循環包含依賴關係,可以很容易地避免。如果我錯了,請糾正我。 – Mephane

+0

@Mephane:我不明白怎麼會有一個循環依賴,因爲「enumeration.td」不包含任何東西......另外宏MY_ENUMERATION_META_FUNCTION並不意味着在枚舉文件中定義,因爲它會阻止它作爲一個元函數使用,所以它會有點挫敗在那裏定義它的目的。 –

+0

現在我明白了。第二個文件不是另一個可以重複使用的代碼,但是是一個實際的實現,每個實現都可以定義它們自己的'MY_ENUMERATION_META_FUNCTION'版本。是的,它完全感覺像一個黑客。能否做這樣的事情是一個實際的好處,當然是相當主觀的,但我現在看到了這種可能性。 – Mephane

0

#pragma曾經存在的問題,以及它不屬於標準的原因是,它並不總是在任何地方都能正常工作。編譯器如果知道兩個文件是否是相同的文件(如果包含在不同的路徑中)如何?

想一想,如果編譯器出錯並且未包含應該包含的文件,會發生什麼情況?如果它包含一個文件兩次,它不應該發生什麼?你會如何解決這個問題?

隨着包括守衛,可能發生的最糟糕的是編譯需要更長的時間。

編輯: 看看這個線程在comp.std.C++「#pragma曾經在ISO標準嗎?」

http://groups.google.com/group/comp.std.c++/browse_thread/thread/c527240043c8df92

+0

我並沒有特別要求'#pragma once'和'#define' include-guard之間的區別,但是對於特別想多次包含文件的有效情況。 – Mephane

+0

好吧,我以爲你問我們爲什麼在默認情況下我們沒有'#pragma once'。就像在最後一句話中一樣...... –

+0

我對自己期望的效果有更多的要求, '#pragma once'是一種實現這種效果的手段,就像包括守衛一樣。 – Mephane