当链接器可以使用显式特化时,所有编译器都会忽略生成的模板代码吗?

时间:2013-02-28 00:31:07

标签: c++ templates

我最近遇到一种情况,同时专门化模板让我感到不安:

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

main.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的链接不会像我想要的那样取代泛型动作。这是一个有效的担心吗?我读了很多令我感到不安的事情,但没有什么能让我对目前情况发生的事情充满信心。

1 个答案:

答案 0 :(得分: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!

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