【问题标题】:Template class member specialization without declaration in header没有在标题中声明的模板类成员特化
【发布时间】:2013-02-25 07:09:30
【问题描述】:

我有一个模板类,我在标头中声明了一个方法,并且在标头中没有定义该方法。在一个 .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++ 标准规定的这种行为?换句话说,这段代码是否可移植?

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

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

【问题讨论】:

  • 这是一个相当好的第一个问题!我认为 IBM 的编译器允许您使用 extern 关键字声明您的特化,但我自己从未这样做过。

标签: c++ templates declaration template-specialization


【解决方案1】:

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

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

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

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

【讨论】:

  • 谢谢。如果主模板中有内联定义,则绝对同意此代码已损坏。但鉴于没有,标准是否说明了在这种情况下会发生什么?那段似乎没有涵盖它。
  • @TristanSchmelcher 我相信上面的部分涵盖了所有情况(除非在我没有看到的地方有例外)。它清楚地表明必须在使用之前声明显式特化。因此,如果头文件没有声明它,但 main.cpp(包括头文件,但不包括 .cc)使用它,则违反标准。
  • 我明白了。我认为术语“隐式实例化”不会涵盖 main.cc 中的使用,因为未专门化 bar() 的定义不在范围内,但 14.7.1 似乎说无论如何它都算作隐式实例化。
猜你喜欢
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多