头中没有声明的模板类成员专用化

Template class member specialization without declaration in header

本文关键字:成员 专用 声明      更新时间:2023-10-16

我有一个模板类,我用一个方法在头中声明它,而在头中没有该方法的定义。在一个.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;
}

该程序成功地编译并链接了适用于所有C++标准(C++98、gnu++98、C++11和gnu++11(的gcc 4.7.2。输出为:

1
2

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

我的问题是:这是一个意外,还是C++标准强制要求这种行为?换句话说,这个代码是可移植的吗?

我能找到的最相关的先前问题是模板类成员专业化的声明,但它没有涵盖这个特定的情况。

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

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

(14.7.3/6(如果模板、成员模板或类模板的成员是明确专门化的,则应在首次使用该专门化之前声明该专门化,这将导致在发生这种使用的每个翻译单元中发生隐式实例化;不需要进行诊断。如果程序没有提供显式专门化的定义,并且专门化的使用方式会导致隐式实例化发生,或者成员是虚拟成员函数程序格式不正确,不需要诊断。对于已声明但未定义的显式专门化,永远不会生成隐式实例化。[…]

我相信,只有当您的主要模板定义包括其中一个成员函数的非专用版本的定义时,这在实践中才会产生效果。因为在这种情况下,当没有声明显式专用化时,现有的主定义可能会被用来将函数内联编译到代码中,而专用化最终将不会在链接时使用。

换言之,如果主模板定义中没有成员函数的定义,那么链接器技巧可能会在实践中发挥作用,但它不符合标准的规定,而且一旦将内联函数定义添加到主模板中,就会给您带来真正的麻烦。