2016-09-25 63 views
2

我的一個隊友經常使用的PIMPL的變化,他確實是這樣的:使用匿名命名空間結構的pimpl習語:這是安全的嗎?

了foo.h:

namespace { struct Impl; } 

class Foo 
{ 
public: 
    Foo(); 
    ~Foo(); 

    void Bar(int n); 
    /* ... */ 

private: 
    std::unique_ptr<Impl> _impl; 
}; 

這裏發生的事情是,他的前瞻性聲明的實現類是在匿名命名空間。然後他將在Foo.cpp中定義Impl類。

所以結構::Impl的定義將可用於Foo.cpp的翻譯單元。其他代碼包括Foo.h將提出警告,因爲他們顯然無法訪問Foo.cpp中定義的::Impl。但是,我們並不需要它們 - 這是一個僅用於Foo.cpp的類;我們不想想要它可見或在其他地方已知。

雖然我們可以肯定有一個情況上.cpp文件包括多個頭,各自宣稱自己::Impl結構,這些實際上並不衝突,因爲結構從未各自的轉換單元之外使用。

tl; dr:這看起來很奇怪,引發警告,看起來好像它可能會引起衝突,但似乎實際上工作。


之所以這麼說,我不舒服有一些提出警告,烘乾,深入到我們的代碼(更多的頭文件,這是在,就越難將是冒了出來。)這是也只是一個的警告。

我的隊友支持這個,因爲它很簡單,保持簡單的代碼定義,並讓我們在我們所有的代碼中使用短而一致的類名Impl

我不是編碼慣例的堅守者;如果這是我們用例的良好實踐,我不介意。但我希望能夠安心,這是安全和可維護的,並且在某些時候不會在我們的臉上炸開。

+0

重點是,我認爲*,沒有':: Impl'。使用':: Impl'嘗試訪問* global *名稱空間中的符號'Impl',但它不在全局名稱空間中。另一個常見的方法是使'Impl'結構成爲類本身的私有成員。 –

+6

如果這發生在多個翻譯單元使用的頭文件中,那麼我認爲這是ODR違規。每個匿名命名空間在每個翻譯單元中都不相同,因此Foo類在所有翻譯單元中都沒有一致的定義。 –

+0

參見http://stackoverflow.com/a/23653494/951890 –

回答

5

該類Foo違反了ODR。每個cpp文件都認爲其獨特的ptr包含不同的類型。

ODR違規使您的程序生病,無需診斷。

你的程序可能工作,但其行爲完全沒有被C++標準指定。

可能導致的實際問題是編譯器可能會在您的腳下發生變化,當前未定義的行爲(「它似乎工作」)將更改爲其他內容(「casts fail unexpectedly」,「損壞的類型表」 「鏈接程序無法鏈接」,「編譯器證明類永遠不能在其實現的翻譯單元之外使用,並且刪除函數中的所有代碼,就好像它們運行它將會是UB一樣」)作爲示例,但是沒有必要它可能會有多瘋狂。

做UB有時候會有好處,值得冒險。我在這裏看到零利益。

創建一個namespace FooImplFoo_details並填入Impl

相關問題