2010-05-09 177 views
13

我的一位同事告訴我他用他的團隊設計的一小塊設計讓我大開眼界。這是一種性狀類,他們可以專門在一個非常分離的方式。未定義的模板方法技巧?

我很難理解它是如何工作的,我仍然不確定我的想法,所以我想我會在這裏尋求幫助。

我們在這裏討論g ++,特別是3.4.2和4.3.2版本(它們似乎適用於兩者)。

的想法是很簡單:

1-定義接口

// interface.h 
template <class T> 
struct Interface 
{ 
    void foo(); // the method is not implemented, it could not work if it was 
}; 

// 
// I do not think it is necessary 
// but they prefer free-standing methods with templates 
// because of the automatic argument deduction 
// 
template <class T> 
void foo(Interface<T>& interface) { interface.foo(); } 

2-定義一個類,並且在源文件中專門接口這個類(定義它的方法)

// special.h 

class Special {}; 


// special.cpp 

#include "interface.h" 
#include "special.h" 

// 
// Note that this specialization is not visible outside of this translation unit 
// 
template <> 
struct Interface<Special> 
{ 
    void foo() { std::cout << "Special" << std::endl; } 
}; 

3到使用,它的簡單太:

// main.cpp 

#include "interface.h" 

class Special; // yes, it only costs a forward declaration 
       // which helps much in term of dependencies 

int main(int argc, char* argv[]) 
{ 
    Interface<Special> special; 
    foo(special); 
    return 0; 
}; 

如果沒有翻譯單元爲Special定義Interface的專用化符號,這是一個未定義的符號。

現在,我會認爲這將需要export關鍵字,據我所知,這在我的知識中從未在g ++中實現(並且只在C++編譯器中實現過一次,其作者建議任何人不要在給定時間和精力的情況下它花了他們)。

我懷疑它有事情做與連接解決方​​法的模板...

  • 你有沒有見過這樣的事之前?
  • 它符合標準嗎?或者您認爲這是巧合嗎?

我必須承認,我對構建挺納悶......

回答

10

像@Steward懷疑,這是無效的。正式地,它是有效的導致未定義的行爲,因爲標準規定違規不需要診斷,這意味着實現可以默默地執行任何想要的事情。在14.7.3/6

如果一個模板,一個成員模板或類模板的成員明確專門那麼專業化應在第一次使用的是專業化的前宣佈,將導致隱式實例發生,在每一個翻譯單位,其中發生這種使用;不需要診斷。

實際上至少在GCC,它隱含實例化的初級模板Interface<T>因爲專業化未聲明是不可見的main,然後調用Interface<T>::foo。如果它的定義是可見的,它會使成員函數的主要定義不變(這就是爲什麼當它被定義時,它不起作用)。

實例化函數名稱符號具有弱連接,因爲它們可能在不同的對象文件中出現多次,並且必須在最終程序中合併爲一個符號。相反,不再是模板的明確專業化成員具有強大的聯繫,所以他們將主導弱的聯繫符號,並使呼叫最終成爲專業化。所有這些都是實施細節,而標準沒有這種弱/強聯繫的概念。你必須專業化申報創建special對象之前:

template <> 
struct Interface<Special>; 

標準奠定了它的裸(由我強調)

明確的專業化聲明函數模板,類模板,成員的位置類模板的功能,類模板的靜態數據成員,類模板的成員類,類模板的成員類模板,類模板的成員函數模板,類模板的成員模板的成員函數,非模板成員模板的成員函數類,成員類的成員函數模板c lass模板等,以及類模板的部分專業化聲明,非模板類的成員類模板,類模板的成員類模板等的放置可以根據相對定位來影響程序是否良好形成在上面和下面指定的翻譯單元中明確的專門化聲明及其實例化點。 在寫專業時,要注意它的位置;或者使之編纂成爲一種試驗,以點燃它的自焚。

+0

因此,如果我理解正確的話,在「special.h」中包含「interface.h」並轉發聲明模板專用化'template <> struct Interface ;'然後在「main.cpp」 「special.h」,以便使程序格式良好,即使'Interface :: foo'方法的定義永遠不會出現? – 2010-05-09 14:30:27

+0

@Matthieu,正好!這就是你可以如何解決不幸的問題 – 2010-05-09 14:35:05

+0

如果我正確地讀了這個,這意味着任何程序(多個翻譯單元),其中對於給定的一組參數存在模板的顯式專門化,但是在一些其他翻譯單元中,隱式專門化用於同一組參數是ODR違規,編譯器不需要 - 也可能不能 - 發佈診斷? 這是非常可怕的,也是另一個很好的理由,即在可能的情況下始終將主要模板部分專業化。 – Stewart 2010-05-09 18:24:27

5

那是相當整潔。我不確定它是否能保證在任何地方都能正常工作。看起來他們正在做的是有一個有意識的未定義的模板方法,然後定義一個隱藏在自己的翻譯單元中的專業化。它們取決於使用相同名稱的編譯器,原來的類模板方法和特化,這是我認爲可能是非標準的。然後鏈接程序將查找類模板的方法,但找到專業化。

雖然有一些風險。沒有人,甚至連接器都不會拿起該方法的多個實現。模板方法將被標記爲selectany,因爲模板意味着內聯,所以如果鏈接器看到多個實例,而不是發出錯誤,它將選擇最方便的一個。

儘管不幸的是,它似乎確實是一個幸運的巧合,它的工作原理。

+0

那麼,任何模板方法定義都會出現鏈接問題,即使我承認專業化風險較高。這裏他們依靠約定(專業化在相應的源文件中)來防止違反一個定義規則。有人可能會說,如果爲通用案例定義了模板方法,那麼將違反ODR,因爲通用模板將被立即執行。關於這個名字的問題很好,我也沒有想到,那裏也可能有問題。 – 2010-05-09 13:12:19