2013-02-06 52 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