2013-02-25 108 views
11

我有一個模板類,我聲明在一個方法的標題與沒有在該標題中的方法的定義。在.cc文件中,我定義了方法的專業化版本,但沒有在頭文件中聲明它們。在一個不同的.cc文件中,我爲不同的模板參數調用了專門化的方法。它看起來像這樣:模板類成員專業化沒有聲明在標頭

了foo.h:

template<typename T> 
class Foo { 
public: 
    static int bar(); 
}; 

foo.cc:

#include "foo.h" 

template<> 
int Foo<int>::bar() { 
    return 1; 
} 

template<> 
int Foo<double>::bar() { 
    return 2; 
} 

main.cc:

#include <iostream> 
#include "foo.h" 

int main(int argc, char **argv) { 
    std::cout << Foo<int>::bar() << std::endl; 
    std::cout << Foo<double>::bar() << std::endl; 
    return 0; 
} 

這個程序用gcc編譯和鏈接成功4.7.2適用於所有C++標準(C++ 98,gnu ++ 98,C++ 11和gnu ++ 11)。輸出是:

1 
2 

這對我有意義。由於main.cc翻譯單元沒有看到bar()或其任何專門化的定義,因此它期望bar()的調用在某些其他翻譯單元中使用明確定義的bar()的非特定定義。但是由於名稱修改是可預測的,因此foo.cc中的專業化具有與非專用定義的顯式實例相同的符號名稱,因此main.cc能夠使用這些專業化,而不會在該翻譯單元中聲明它們。

我的問題是這樣的:這是一個意外,還是這種行爲由C++標準強制?換句話說,這個代碼是否便攜?

我能找到的最相關的以前的問題是Declaration of template class member specialization,但它不包括這個特殊情況。 (如果你想知道爲什麼這對我來說很重要,那是因爲我使用這樣的代碼作爲一種編譯時查找表,如果我沒有聲明專門化,它會縮短很多。)

+2

這是一個相當不錯的第一個問題!我認爲IBM的編譯器允許您使用extern關鍵字聲明專業化,但從未自己完成。 – 2013-02-25 07:15:09

回答

8

標準(C++ 11)要求明確的特化被宣佈(但不一定限定)首先被使用之前:

(14.7.3/6)如果模板,一個成員模板或類模板的成員是明確專門化的,那麼專業化應在首次使用該專業化之前宣佈,該專業化將導致隱式實例化,在每個tra中其中發生這種使用的消隱單元;不需要診斷。如果程序沒有爲明確的專業化提供定義,並且專業化的使用方式會導致隱式實例化或成員是虛擬成員函數,那麼程序格式不正確,不需要診斷。對於已聲明但未定義的顯式特化,永遠不會生成隱式實例化。 [...]

我相信這實際上只會在您的主模板定義包含其中一個成員函數的非專用版本的定義時才起作用。因爲在這種情況下,當沒有聲明明確的專門化時,可以使用現有的主要定義將函數內聯編譯到代碼中,專業化最終不會在鏈接時使用。換句話說,如果主模板定義中沒有包含成員函數的定義,則可能預期鏈接器技巧在實踐中可行,但不符合標準所說的內容,而且它也不符合只要將內聯函數定義添加到主模板,就可以讓您陷入真正的麻煩。

+0

謝謝。絕對同意,如果主模板中有嵌入式定義,則此代碼被破壞。但是鑑於沒有,標準是否會說明在這種情況下會發生什麼?該段似乎沒有涉及。 – 2013-02-25 19:50:41

+0

@TristanSchmelcher我相信上面的部分涵蓋了所有的情況(除非有什麼例外,我沒有看到)。它清楚地表明,明確的專業化必須在使用之前進行申報。因此,如果頭文件沒有聲明它,但main.cpp(包含頭文件,但不包含.cc)使用它,它違反了標準。 – jogojapan 2013-02-26 00:53:35

+1

我明白了。我認爲術語「隱式實例化」不會涵蓋main.cc中的用法,因爲沒有定義非特殊化的bar()是在範圍之內,但14.7.1似乎認爲它不管是否隱式實例化。 – 2013-02-27 20:35:04