我希望有人可以指出在模板类中专门化方法的正确方法,同时使用“extern模板类”和“模板类”进行gnu c ++的显式实例化。我试图通过模仿我真实问题的最简单的例子来解决这个问题。似乎声明“extern模板”意味着模板实例化,这在专门化方法时会导致错误。给出一个驱动程序:
main.cc
#include A_H
#include <iostream>
int main()
{
A<int> ai;
A<long> al;
std::cout << "ai=" << ai.get() << " al=" << al.get() << std::endl;
return 0;
}
以下实施A
A.H
template<typename T>
struct A
{
int get() const;
};
extern template class A<int>;
extern template class A<long>;
a.cc
#include "a.h"
template<typename T>
int A<T>::get() const
{
return 0;
}
template<>
int A<long>::get() const
{
return 1;
}
template class A<int>;
template class A<long>;
使用g ++ 4.1.2或4.4.4
进行编译时收到以下错误 % g++ -Wall -g -D'A_H="a.h"' a.cc main.cc
a.cc:10: error: specialization of 'int A<T>::get() const [with T = long int]' after instantiation
%
如果我在a.h中注释掉两个“extern模板”行,那么两个编译器都会按预期编译和工作。我假设在没有“extern模板”的情况下存在明确的实例化,即使在C ++ 0x中也是未指定的行为,否则,C ++ 0x添加“extern模板”的意义是什么?
如果我改为将A实现为:
A-hack.h
template<typename T>
struct A
{
int get() const;
};
template<typename T>
int A<T>::get() const
{
return 0;
}
template<>
inline
int A<long>::get() const
{
return 1;
}
extern template class A<int>;
extern template class A<long>;
a-hack.cc
#include "a-hack.h"
template class A<int>;
template class A<long>;
再次编译,这可以按预期工作
% g++ -Wall -g -D'A_H="a-hack.h"' a-hack.cc main.cc
% ./a.out
ai=0 al=1
然而,在我的真实世界的例子中,这导致程序崩溃与g ++ 4.1.2(同时为g ++ 4.4.4工作)。我没有缩小崩溃的确切原因(分段错误)。它似乎只是在对A&lt;&gt; :: get()的调用中,堆栈指针被破坏。
我意识到显式模板实例化在这一点上是非标准的,但是有人会期望我上面所做的工作吗?如果没有,这样做的正确方法是什么?
由于
答案 0 :(得分:16)
extern template class A<long>;
这一行说明A<long>
将根据编译器已经看到的定义显式实例化。稍后添加专业化时,就会破坏其含义。
在头文件中添加专业化声明。
template <typename T> struct A { /*...*/ };
template<> int A<long>::get() const;
extern template class A<int>;
extern template class A<long>;
通常,最好在与主模板相同的头文件中放置尽可能多的特化声明,以减少编译器对于任何特定实例化应该使用哪个声明的意外。
请注意,如果您正在处理单个模板实体,则不需要extern template
声明(与此情况相反,我们必须向编译器指示类 A<long>
和功能 A<long>::get()
)。如果您想在另一个翻译单元中专门化一个功能模板,只需编写template<>
即可。
template<typename T> int freeGet() { return 0; } // you can even add "inline" here safely!
template<> int freeGet<long>(); // this function is not inline (14.7.3/12)
但你必须拥有<>
。如果省略<>
,声明将变为默认实现(return 0
)的显式实例化,这可能不是您想要的!即使添加extern
,编译器也可以内联该默认实现;如果您的代码在通过-O2
时意外中断,您可能会在某处意外省略<>
。
答案 1 :(得分:3)
添加此答案以解决标题中的问题(模板实例化,而不一定是模板方法实例化)。
这非常类似于函数声明/定义。
extern template class
是一个声明,通常应该放在标题中。template class
是一个定义,通常应该放在cpp中。更多信息here。