13

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

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

主.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++ 标准规定的这种行为?换句话说,这段代码是否可移植?

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

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

4

1 回答 1

9

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

(14.7.3/6) 如果模板、成员模板或类模板的成员被显式特化,则应在第一次使用该特化之前声明该特化,这将导致在每次翻译中发生隐式实例化发生此类使用的单位;不需要诊断。如果程序没有为显式特化提供定义,并且该特化的使用方式会导致发生隐式实例化,或者该成员是虚拟成员函数,则该程序是非良构的,不需要诊断。永远不会为已声明但未定义的显式特化生成隐式实例化。[...]

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

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

于 2013-02-25T07:21:29.857 回答