标头中没有声明的模板类成员特化

Tri*_*her 12 c++ templates declaration template-specialization

我有一个模板类,我在一个标题中声明了一个方法,并且标题中没有该方法的定义.在.cc文件中,我定义了该方法的特化,而没有在标题中声明它们.在另一个.cc文件中,我为不同的模板参数调用该方法,其中存在特化.它看起来像这样:

foo.h中:

template<typename T>
class Foo {
public:
  static int bar();
};
Run Code Online (Sandbox Code Playgroud)

foo.cc:

#include "foo.h"

template<>
int Foo<int>::bar() {
  return 1;
}

template<>
int Foo<double>::bar() {
  return 2;
}
Run Code Online (Sandbox Code Playgroud)

main.cc:

#include <iostream>
#include "foo.h"

int main(int argc, char **argv) {
  std::cout << Foo<int>::bar() << std::endl;
  std::cout << Foo<double>::bar() << std::endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

对于所有C++标准(c ++ 98,gnu ++ 98,c ++ 11和gnu ++ 11),该程序与gcc 4.7.2成功编译和链接.输出是:

1
2
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义.因为main.cc转换单元没有看到它的定义bar()或任何特化,所以它期望调用在一些其他转换单元bar()中使用非bar()特定定义的显式实例化.但由于名称修改是可预测的,因此foo.cc中的特殊化具有与非专用定义的显式实例化相同的符号名称,因此main.cc能够使用这些特化而不会在该转换单元中声明它们.

我的问题是:这是一个意外,还是C++标准规定的这种行为?换句话说,这个代码是否可移植?

我能找到的最相关的先前问题是模板类成员专业化的声明,但它不包括这个特定情况.

(如果您想知道为什么这对我来说很重要,那是因为我使用这样的代码作为一种编译时查找表,如果我不声明特化,它会更短.)

jog*_*pan 8

标准(C++ 11)要求在首次使用之前声明(但不一定定义)显式特化:

(14.7.3/6)如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致在每个翻译中发生隐式实例化.发生这种用途的单位; 无需诊断.如果程序没有提供显式特化的定义,并且特殊化的使用方式会导致隐式实例化或成员是虚拟成员函数,则程序格式错误,无需诊断.永远不会为已声明但未定义的显式特化生成隐式实例化.[...]

我相信,当您的主模板定义包含其中一个成员函数的非专用版本的定义时,实际上这只会产生影响.因为在这种情况下,当未声明显式特化时,可以使用现有的主要定义将函数内联编译到代码中,并且特殊化最终不会在链接时使用.

换句话说,如果主模板定义中没有包含成员函数的定义,那么您的链接器技巧可能会在实践中发挥作用,但它不符合标准所说的内容,它可以让您进入一旦将内联函数定义添加到主模板,就会出现麻烦.