C++函数模板的特化可见性

25
假设我有一个名为fileA.h的文件,它声明了一个带有模板函数SomeFunc<T>()classA类。这个函数在头文件中直接实现(对于模板函数来说通常是这样)。现在我在fileA.C中添加了SomeFunc()的专门实现(例如SomeFunc<int>()),而不是在头文件中添加。
如果我现在从一些其他代码中调用SomeFunc<int>()(可能也来自另一个库),它会调用通用版本还是专门版本?
我现在遇到了这个问题,其中类和函数位于一个库中,该库由两个应用程序使用。一个应用程序正确使用了专门实现,而另一个应用程序使用了通用形式(这会在以后引起运行时问题)。为什么会出现这种差异?这可能与连接器选项等有关吗?这是在Linux上,使用的是g++ 4.1.2。
9个回答

23

如果在调用点不可见的模板中为其定义特化是错误的。不幸的是,编译器没有必要诊断这个错误,因此可以对你的代码进行任何操作(按标准术语来说,它是“非法的,不需要诊断”)。

从技术上讲,你需要在头文件中定义这个特化,但几乎每个编译器都会按照你的期望处理这个问题:C++11 中有一个新的“extern 模板”工具来解决这个问题:

extern template<> SomeFunc<int>();

这明确声明特定的专业化定义在其他地方。许多编译器已经支持此功能,有些使用extern,有些则不使用。


9

你是否在头文件中添加了带参数的原型?

我的意思是,文件A.h中是否有这样的地方:

template<> SomeFunc<int>();

如果不是那个原因的话,那很可能就是问题所在。

3

我也遇到了gcc4的同样问题,以下是我解决它的方法。相比之前的评论所说的,我的解决方案更加简单。之前的帖子提供的思路是正确的,但是它们的语法对我不起作用。


    ----------header-----------------
    template < class A >
    void foobar(A& object)
    {
      std::cout << object;
    }

    template <> 
    void foobar(int);

    ---------source------------------
    #include "header.hpp"

    template <>
    void foobar(int x)
    {
      std::cout << "an int";
    }


2
根据规范,除非您导出模板定义(除Comeau之外的任何编译器都不支持或预计在可预见的未来内支持),否则您的专用函数模板不应在fileA.C以外被调用。
另一方面,一旦函数模板被实例化,编译器将看到一个不再是模板的函数。GCC可能会在不同的编译单元中重复使用此定义,因为标准规定对于给定类型参数集,每个模板只能被实例化一次[temp.spec]。尽管如此,由于该模板未被导出,因此应将其限制在编译单元中。
我认为,GCC可能会在跨编译单元共享已实例化模板列表时存在错误。通常,这是一个合理的优化,但它应该考虑函数专门化,而似乎没有正确地执行。

1
正如Anthony Williams所说,extern template结构是正确的方法,但由于他的示例代码不完整且存在多个语法错误,因此这里提供了完整的解决方案。

fileA.h:

namespace myNamespace {
  class classA {
    public:
      template <class T> void SomeFunc() { ... }
  };

  // The following line declares the specialization SomeFunc<int>().
  template <> void classA::SomeFunc<int>();

  // The following line externalizes the instantiation of the previously
  // declared specialization SomeFunc<int>(). If the preceding line is omitted,
  // the following line PREVENTS the specialization of SomeFunc<int>();
  // SomeFunc<int>() will not be usable unless it is manually instantiated
  // separately). When the preceding line is included, all the compilers I
  // tested this on, including gcc, behave exactly the same (throwing a link
  // error if the specialization of SomeFunc<int>() is not instantiated
  // separately), regardless of whether or not the following line is included;
  // however, my understanding is that nothing in the standard requires that
  // behavior if the following line is NOT included.
  extern template void classA::SomeFunc<int>();
}

fileA.C:

#include "fileA.h"

template <> void myNamespace::classA::SomeFunc<int>() { ... }

1
在Microsoft C++中,我进行了一项有关内联函数的实验。我想知道如果在不同的源文件中定义不兼容版本的函数会发生什么。根据是使用Debug构建还是Release构建,我得到了不同的结果。在Debug中,编译器拒绝内联任何东西,链接器将链接相同版本的函数,无论源文件中的作用域为何。在Release中,编译器会内联在当时已经定义的版本,并且你会得到不同版本的函数。
在两种情况下都没有警告。我有点怀疑这一点,这就是我进行实验的原因。
我假设模板函数会表现相同,其他编译器也会是如此。

0

除非专用的模板函数也在头文件中列出,否则其他应用程序将不知道专用版本。解决方案是将SomeFunc<int>()添加到头文件中。


0

Brandon: 我也是这么想的 - 专门的函数不应该被调用。这对我提到的第二个应用程序是正确的。然而,第一个应用程序明显调用了专门的形式,尽管在头文件中没有声明这种特化!

我主要在这里寻求启示 :-) 因为第一个应用程序是单元测试,很不幸的是,在测试中没有出现但在实际应用程序中出现了错误...

(PS:我已经通过在头文件中声明特化来修复了这个具体的错误;但还有哪些类似的错误可能仍然隐藏着呢?)


0

@[anthony-williams],

你确定你没有把extern模板声明和extern template实例化搞混了吗?从我看到的情况来看,extern template只能用于显式实例化,而不能用于特化(这意味着隐式实例化)。[temp.expl.spec]中并没有提到extern关键字:

explicit-specialization:
    template < > declaration


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接