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

时间:2013-02-25 07:09:31

标签: c++ templates declaration template-specialization

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

foo.h中:

template<typename T>
class Foo {
public:
  static int bar();
};

foo.cc:

#include "foo.h"

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

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

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;
}

该程序使用gcc 4.7.2成功编译并链接所有C ++标准(c ++ 98,gnu ++ 98,c ++ 11和gnu ++ 11)。输出是:

1
2

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

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

我能找到的最相关的先前问题是Declaration of template class member specialization,但它不包括这个特殊情况。

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

1 个答案:

答案 0 :(得分:8)

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

  

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

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

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