编译器如何知道使用模板特化而不是自己的实例化?

Luk*_*uke 3 c++ templates specialization

请考虑以下文件:

foo.h中

template <typename T>
struct Foo
{
  int foo();
};

template <typename T>
int Foo<T>::foo()
{
  return 6;
}
Run Code Online (Sandbox Code Playgroud)

foo.c的

#include "Foo.H"

template <>
int Foo<int>::foo()
{
  return 7;
}
Run Code Online (Sandbox Code Playgroud)

MAIN.C

#include <iostream>
#include "Foo.H"

using namespace std;

int main()
{
  Foo<int> f;
  cout << f.foo() << endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我编译并运行时,打印7.这里发生了什么?什么时候模板实例化?如果编译器这样做,编译器如何知道不实例化它自己的Foo版本?

Jos*_*vin 17

问题是您违反了一个定义规则.在main.C中,你已经包含了Foo.H但不包括Foo.C(这是有意义的,因为它是一个源文件).编译main.C时,编译器不知道你在Foo.C中专门设置了模板,因此它使用泛型版本(返回6)并编译Foo类.然后,当它编译foo.c的,它看到一个完整的专业化,它可以编译马上 -它不需要等待它在某处被实例化,因为所有的类型都填充(如果你有两个模板参数,只专门的,这不是这种情况),它编译一个新的和不同的 Foo类.

通常,同一事物的多个定义会导致链接器错误.但模板实例化是"弱符号",这意味着允许多个定义.链接器假设所有定义都是相同的,然后随机选择一个(好吧,可能始终是第一个或最后一个,但只是作为实现的巧合).

为什么要让它们变弱?因为Foo可能在多个源文件中使用,每个源文件都是单独编译的,并且每次在编译单元中使用Foo时都会生成新的实例化.通常情况下,这些都是多余的,因此将它们扔掉是有道理的.但是你违反了这个假设,通过在一个编译单元(foo.C)中提供专门化而不是另一个(main.C).

如果在Foo.H中声明模板特化,那么当编译main.C时,它不会生成Foo的实例化,因此确保程序中只存在一个定义.

  • 有些人可能不知道这个的语法.您可以通过编写`template <> int Foo <int> :: foo();`来实现,只需省略正文. (3认同)
  • 谢谢:优秀的答案.这是否意味着前向声明专门化(在头文件中)将抑制main.C中Foo <int>的生成? (2认同)