【问题标题】:What's the right way to specialize a template when using "extern template"?使用“外部模板”时专门化模板的正确方法是什么?
【发布时间】:2011-03-18 18:25:10
【问题描述】:

我希望有人可以指出在使用“extern 模板类”和“模板类”进行 gnu c++ 显式实例化的同时,在模板类中专门化方法的正确方法。我试图用模仿我真正问题的最简单的例子来解决这个问题。似乎声明“外部模板”意味着模板实例化,它在专门化方法时会导致错误。给定一个驱动程序:

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的实现

啊。

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 中的两个“外部模板”行,两个编译器都会按预期编译和工作。我假设在没有“外部模板”的情况下存在显式实例化是未指定的行为,即使在 C++0x 中也是如此,否则,C++0x 添加“外部模板”有什么意义?

如果我将 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::get() 的调用中被破坏了。

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

谢谢

【问题讨论】:

  • c++0x 去掉了extern模板,因为太难实现了吧?
  • @VJo: export 已删除。 extern(用于模板)已添加。
  • @James 一定是新的东西,因为到目前为止我还没有看到导出的模板
  • @VJo:是的,它是 C++0x 中的新功能,尽管一些编译器已经支持它作为扩展已有一段时间了。自 2005 年以来,Visual C++ 就拥有它。
  • @VJo, @James:詹姆斯的最后回复 [20:44] 似乎将两个不同的事情混为一谈(他对 [18:40] 的评论更清晰)。 export 模板由 EDG 实现,在 C++03 中标准化,并在没有其他编译器供应商使用它们后在 C++11 中删除。 EDG/GCC/IBM/Metrowerks/MSVC 自 2003 年以来一直支持 extern 模板(来源:N1448,另请参阅 N1987),但仅在 C++11 中标准化(类似于 @987654334 发生的情况@)。

标签: c++ templates c++11


【解决方案1】:
extern template class A<long>;

这一行表示A&lt;long&gt; 将根据编译器已经看到的定义显式实例化。当您稍后添加专业化时,您会破坏该含义。

在头文件中添加你的专业化声明。

template <typename T> struct A { /*...*/ };
template<> int A<long>::get() const;
extern template class A<int>;
extern template class A<long>;

一般来说,最好将尽可能多的特化声明放在与主模板相同的头文件中,以减少编译器对于任何特定实例化应该使用哪个声明的意外。


请注意,如果您正在处理单个模板实体,则不需要 extern template 声明(与这种情况相反,在这种情况下,我们必须向编译器指示 @ 987654325@ 和 函数 A&lt;long&gt;::get())。如果你想在另一个翻译单元中专门化一个函数模板,只写template&lt;&gt;就足够了。

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)

但你必须有&lt;&gt;。如果您省略&lt;&gt;,则声明会变成default 实现的显式实例化(return 0),这可能不是您想要的!即使您添加extern,编译器也可以内联该默认实现;如果您的代码在您传递-O2 时意外中断,您可能不小心在某处遗漏了&lt;&gt;

【讨论】:

  • 就是这样,谢谢!您是否知道为什么“外部模板”或其依赖项需要关心专业化的存在?无论如何,方法的“名称”都是 int A::get() const,并且显式实例化可以确保在链接时只有正确的定义可用。
  • @Roland 通常,您可以执行template &lt;&gt; class A&lt;long&gt; { int iHaveNoGetFunctionAtAll(); }; 之类的操作,这样实际上甚至不存在A&lt;long&gt;::get() 函数!这可能是编译器需要在应用“外部模板”之前声明规范的原因。
  • 很好的答案,但是您能否添加将示例的第 2 行和第 4 行合并到单行 extern template&lt;&gt; int A&lt;long&gt;::get() const; 中是否合法?这就是我来这里希望回答的问题。
  • @Quuxplusone:不是那种语法。如果您想要明确的专业化(您必须定义),请使用template&lt;&gt; int A&lt;long&gt;:get() const; 版本。如果您想要显式实例化(通过模板定义),请不要在template 之后放置任何括号:template int A&lt;long&gt;::get() const;(在前面添加extern 使其不是定义)。
  • 如果我想要一个明确的 extern specialization,但又想在一个声明中表达这个概念怎么办?不可能?
【解决方案2】:

添加此答案以解决标题中的问题(模板实例化,不一定是模板方法实例化)。

这很像函数声明/定义。

  • 显式实例化声明:extern template class 是一个声明,通常应放在标题中。
  • 显式实例化定义:template class 是一个定义,通常应该放在 cpp 中。
  • 声明后的目标文件中不会生成任何代码。
  • 在包含定义的 cpp 的目标文件中生成代码。
  • 如果没有显式实例化,则在实际使用模板时会发生隐式实例化。这将发生在使用模板的每个编译单元(目标文件)中。
  • 如果遇到声明,则不会进行隐式实例化。这是该机制的要点 - 避免由于编译器不相信有一个编译单元负责实例化模板而发生的隐式实例化导致的目标代码重复。

更多信息here.

【讨论】:

  • 您的意思是template class 应该放在与声明关联的 .cpp 文件中,还是放在在其他地方使用模板的 cpp 文件中?
  • arne-mertz.de/2019/02/extern-template-reduce-compile-times 有一个你的意思的例子,我认为它应该放在相关的 .cpp 中,而不是只使用它的 cpp,谢谢
  • 是的,你没看错——它通常应该放在相关的 cpp 中。如果从那里不清楚的话,这就是答案的意图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多