6

我最近在专门设计模板时遇到了一种情况,这让我感到不安:

富.h:

template <class T>
void foo() {
  std::cout << "This is the generic foo" << std::endl;
}

foo.cc:

#include "foo.h"
template <>
void foo<int>() {
  std::cout << "This is foo<int>" << std::endl;
}

主.cc:

#include "foo.h"

int main() {
  foo<int>();
}

所以。我编译如下:

g++ -c main.cc
g++ -c foo.cc
g++ -o main main.o foo.o

输出是"This is foo<int>"。我喜欢这个输出。但我担心我所观察到的可能是 gcc 所独有的(我无法访问其他编译器,所以我无法检查)。

这是我认为 gcc 正在做的事情:编译 main.cc 时,我希望它为 foo 调用发出通用代码,因为它不知道 foo.cc 中的特化。但是通过与 foo.o 链接,它使用了特化,因为它具有相同的签名。

但这值得指望吗?我担心其他编译器(甚至可能是不同版本的 gcc?)在发出模板代码时可能会破坏他们的签名,以某种方式与 foo.o 链接不会像我想要的那样替换通用操作。这是一个有效的担忧吗?我读过很多让我感到不安的东西,但没有任何东西能让我对目前情况下发生的事情充满信心。

4

1 回答 1

8

我担心我所观察到的可能是 gcc 所独有的(我无法访问其他编译器,所以我无法检查)。

你有充分的理由担心:你的程序格式不正确,编译器甚至不需要告诉你!

C++11 标准的第 14.7.3/6 段规定:

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

为了使程序具有一致的行为,您的专业化必须从实例化的角度可见。在您的情况下,它不是:您将其降级到其他翻译单元不包含的文件中。

第 14.7.3/7 段标准非常明确地说明了当你不这样做时会发生什么:

函数模板,类模板,类模板的成员函数,[...] 的显式特化声明的放置可以根据显式特化声明的相对定位及其实例化点影响程序是否格式良好。上面和下面指定的翻译单元。编写专业时,请注意其位置;或者让它编译将是一种试炼,以点燃它的自焚。

我想最后一句话说清楚了。

在这里,您应该做的是声明您打算在主模板的任何隐式实例化发生之前引入函数模板的显式特化。为此,请执行以下操作:

foo.h

template <class T>
void foo() {
   std::cout << "This is the generic foo" << std::endl;
}

template <> void foo<int>(); // Introduce a declaration of your
                             // explicit specialization right
                             // after you defined the primary
                             // template!

通过在主模板的定义之后立即引入声明,您可以确保无论主模板在哪里可见,都会知道它存在完整的专业化,从而使您免于自焚。

于 2013-02-28T00:40:11.993 回答