使用“extern模板”时,对模板进行专门化的正确方法是什么?

时间:2011-03-18 18:25:11

标签: c++ templates c++11

我希望有人可以指出在模板类中专门化方法的正确方法,同时使用“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()的调用中,堆栈指针被破坏。

我意识到显式模板实例化在这一点上是非标准的,但是有人会期望我上面所做的工作吗?如果没有,这样做的正确方法是什么?

由于

2 个答案:

答案 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中。
  • 声明后的目标文件中没有生成代码。
  • 代码在包含定义的cpp的目标文件中生成。
  • 如果没有显式实例化,则在实际使用模板时将进行隐式实例化。这将发生在使用模板的每个编译单元(目标文件)中。
  • 如果遇到声明,则不会发生隐式实例化。这是这种机制的要点 - 避免因隐式实例化而导致的目标代码重复,因为编译器并不相信有一个编译单元负责实例化模板。

更多信息here