2013-10-25 28 views
16

我只使用特定於C++的頭文件(例如<cstdlib>),但是我仍然獲得全局聲明的函數,而不僅僅是std命名空間中的函數。有沒有辦法,也許是一個編譯器開關,以防止這種情況?防止std命名空間之外的標準函數

例如,下列代碼:

#include <cstdlib> 
float random() { return 0.0f; } 
int main() { return 0; } 

失敗linux下進行編譯,用下面的錯誤:

> g++ -c main.cpp main.o 
main.cpp: In function ‘float random()’: 
main.cpp:2:14: error: new declaration ‘float random()’ 
/usr/include/stdlib.h:327:17: error: ambiguates old declaration ‘long int random()’ 

> clang++ main.cpp -o main.o 
main.cpp:2:7: error: functions that differ only in their return type cannot be overloaded 
float random() { return 0.0f; } 
/usr/include/stdlib.h:327:17: note: previous declaration is here 
extern long int random (void) __THROW; 

其引起的stdlib.h「污染「全球名稱空間與自己random函數。

請注意,在Windows上使用Visual Studio進行編譯時,我沒有遇到這些問題。

+1

請注意'隨機'是**不是** C標準庫的一部分。這並不是說問題不是現實的。 –

+0

@PeteBecker事實上,它來自[POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/random.html)。使事情變得更有價值,不僅要注意C標準名稱,還要注意所有POSIX的東西。 :( –

回答

10
  1. <cstdlib>將始終填充std名字空間,有時定義全局的符號,而<stdlib.h>總是定義全局符號,有時填充std名字空間。這從實施到實施都不相同。

  2. 標準寫道:

    Every C header, each of which has a name of the form name.h , behaves as if each name placed in the standard library namespace by the corresponding cname header is placed within the global namespace scope. It is unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace std and are then injected into the global namespace scope by explicit using-declarations (7.3.3).

    這意味着,編譯器允許把這些符號爲全局範圍和std命名空間同時

  3. 因此,我們發現優先選擇一個頭文件沒有優勢。因爲他們都很可能污染全球範圍

    但是,它仍然需要使用時std命名空間#include <cstdlib>,並且不使用std#include <stdlib.h>,以確保您的代碼可以編譯所有的編譯器實現。

  4. 建議:請勿在標準庫中使用名稱。首先,他們不能保證工作。 (注意:很少有編譯器實現實際上保持全局作用域的清潔,因此絕不會依賴於此)。其次,它會混淆代碼讀取器和維護者,因爲幾乎每個人都會認爲標準名稱實際上是標準名稱,不管它們來自哪裏。

+2

順便說一下,C++ 98不允許''污染全局名稱空間,C++引入了權限,因爲很多實現都難以滿足要求(如果您不控制,很難或不可能實現C庫也是如此,我似乎記得libstdC++人描述這些困難的一篇論文,但我現在沒有找到它)。 – AProgrammer

6

您可以在自己的名稱空間中聲明您的函數以防止聲明衝突。

namespace MyFunc 
{ 
float random() { return 0.0f; } 
}; 
+0

我同意,這是一個很好的解決方法,並且總的來說是一個很好的編碼實踐,可以將項目放在單獨的名稱空間中。但是,我正在使用已經存在的代碼,嘗試從Windows到Linux,我不想改變超過它的絕對必要 – CygnusX1

0

通常我寧願保持你的函數名稱不同於定義爲標準的名稱。 對於這裏的例子,可以使用函數名稱作爲myRandom而不是隨機的,以便我可以通知後面將維護我的代碼的人員使用的函數不是定義爲標準的函數。

2

該標準明確允許<c???>標題將C標準函數的名稱帶到全局名稱空間。

+3

在我看來,把名字放入全局命名空間中首先會讓'std'命名空間失敗,我想這個權限是爲了向後兼容。請注意,標準允許,但不是必需的,所以仍然可能有一個開關將其關閉。 – CygnusX1

+1

無論原因是什麼,標準都會執行它的功能,因此便攜式代碼不得與C函數發生名稱衝突。 –

3

一般來說,你應該儘量避免重新宣佈。 您可以通過使用命名空間或將源文件分割成文件,其中包括cstdlib和其他可以使用(名稱衝突)函數的static版本的文件。

如果這不是一個選項,然後繼續閱讀。但請注意,以下內容可能非常適合平臺。

通過只是隨便看看我cstdlibstdlib.h在這裏我的地方,我注意到,有由cstdlib決定,如果它包括stdlib.h或只是聲明abort,在std命名空間atextexit的開關。

顯然你拉stdlib.h分支。進一步查看這個文件,我注意到宏__BEGIN_NAMESPACE_STD和更高版本__END_NAMESPACE_STD。也許你可以使用它,但它(如名字所示)是一些實現的內部宏,不應該由你直接設置。但是,它應該在那裏出於某種原因,所以你可能有運氣搜索它。

經過一番搜索後,結果發現random是幾個不包含在__BEGIN_NAMESPACE_STD中的函數(和聲明)之一。因此,這不是解決問題的辦法。 (我發現另一個宏_GLIBCPP_USE_NAMESPACES似乎也在內部使用,以及#define __BEGIN_NAMESPACE_STD namespace std {)。

所以總結一下:這是沒有可行的路徑,你應該使用其中一種描述的解決方法。

+0

While宏本身是內部的,也許有一些「公共」編譯器開關或另一個宏可以打開它? – CygnusX1

+0

這就是我的想法,但我hav e還沒有找到(我在寫回答時開始搜索)。我也不能保證一切都很好,但乍一看看起來很有希望。 – Nobody