2013-02-06 83 views
0

...除非調用庫中的其他內容。這是一個簡單的例子。非模板成員函數的顯式專業化不稱爲

test1.cpp

#include <iostream> 

void proofOfTwoLinked(); 

template <class T> 
struct Foo 
{ 
    void bar(){ std::cout << "normal bar\n"; } 
}; 

struct A{}; 
struct B{}; 
struct C{}; 
struct D{}; 

template <> void Foo<B>::bar(){ std::cout << "B bar\n"; } 

int main() 
{ 
    Foo<A> a; 
    Foo<B> b; 
    Foo<C> c; 
    Foo<D> d; 

    a.bar(); 
    b.bar(); 
    c.bar(); 
    d.bar(); 

    //proofOfTwoLinked(); 
} 

測試2.cpp

#include <iostream> 

struct C; 

template <class T> 
struct Foo 
{ 
    void bar(){ std::cout << "normal bar\n"; } 
}; 

template <> void Foo<C>::bar(){ std::cout << "C bar\n"; } 

void proofOfTwoLinked() 
{ 
    std::cout << "Yup, two is linked\n"; 
} 

如果我編譯兩人在一起時,程序按預期工作:

$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; g++ -o test test1.o test2.o; ./test 
normal bar 
B bar 
C bar 
normal bar 

如果我編譯test2的,放它在一個檔案中,然後將該程序與該程序進行鏈接... C類型的特化不會在c.bar()正所謂:

$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; ar -r test2.a test2.o; g++ -o test test1.o test2.a; ./test 
ar: creating test2.a 
normal bar 
B bar 
normal bar 
normal bar 

但是,如果我去掉TEST1的最後一個函數調用(proofOfTwoLinked),然後重新編譯,專業化是執行

$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; ar -r test2.a test2.o; g++ -o test test1.o test2.a; ./test 
ar: creating test2.a 
normal bar 
B bar 
C bar 
normal bar 
Yup, two is linked 

這讓我覺得很奇怪,而且肯定與我的期望相反。這實際上是正常的行爲?也許因爲在鏈接器搜索test2.a之前,在main()中調用的每個函數都已經存在某種形式,它將跳過該存檔。有沒有辦法強制鏈接器「查看整個檔案」?

我使用gcc 4.6.1和ar 2.21.53(在Ubuntu中)。

+0

此外,很明顯,如果我在test1中聲明'template <> void Foo :: bar();'那麼它也可以工作。我想這足以解決我在實際工作中使用的問題(即只是在頭文件中聲明特化)......但我仍然對它很好奇。 – cheshirekow

+0

除少數例外模板需要放入標題中。模板代碼是在編譯時需要時生成的,因此它們不能輕易打包爲庫(注意:某些編譯器爲此提供瞭解決方法,但它們不可移植)。 –

+0

因此,模板本身在「標題」中(注意兩個.cpp文件中的模板聲明都相同,模擬標題的同時使我的文章變小)。問題是成員函數的特殊性,我猜也必須在標題中。 – cheshirekow

回答

2

使用MSVC2010SP1我得到略有不同的結果:

編譯在一起,是我不明白「C欄」。這是所期望的,因爲test1.cpp和test2.cpp是單獨的編譯單元,沒有包含test1.cpp的特化的前向聲明將實例化其默認的「標準欄」,test2.cpp不會實例化「C酒吧「,因爲它看不到任何使用它。

當我uncomment proofOfTwoLinked();我得到了「Yup,two is linked」,這是預期的,因爲「proofOfTwoLinked()」是前向聲明的。我還是當我再次編譯加入

template <> void Foo<C>::bar(); 

沒有得到「C欄」,這是因爲預期的,因爲它不是向前test1.cpp

declaired到test1.cpp我得到一個鏈接錯誤,因爲雖然test1.cpp編譯單元現在知道有一種

template <> void Foo<C>::bar() 

在某處,測試2.cpp仍然不知道誰在使用它。

當我編譯再次加入

template void Foo<C>::bar(); 

到測試2.cpp一切正常,我也得到「C欄」。請注意,

template void Foo<C>::bar(); 

必須在其定義之前。

據我所知,MSVC的行爲正確,gcc在你的情況下表現怪異。我用http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf第14.7節作爲參考,它可能會有所幫助。

+0

我發佈了一個最小的例子(以及編譯和測試的命令!)來說明問題,所以我不確定你可能會認爲缺少什麼。當你說你不明白main()如何看待專業化時,那正是我不瞭解的。另外,Foo 不是專門的,只有Foo :: bar()。 Foo 包含來自基本模板的所有相同的數據成員和其他成員函數(儘管在本例中......沒有其他成員函數)。把它想象成一個「靜態覆蓋」...沒有vtable要求。 – cheshirekow

+0

好吧,也許我太快了,我離開我的編譯器'直到明天,如果沒有人回答,那麼我會看看如果我不能得到它的工作。 – odinthenerd

+0

現在我的答案應該完整 – odinthenerd