C++ 函数模板专用化的可见性

Visibility of template specialization of C++ function

提问人:oliver 提问时间:9/12/2008 更新时间:10/24/2013 访问量:8651

问:

假设我有 which 声明了一个带有模板函数的类。此函数直接在头文件中实现(通常用于模板函数)。现在,我在(即不在头文件中)中添加了一个专门的实现(如for)。fileA.hclassASomeFunc<T>()SomeFunc()SomeFunc<int>()fileA.C

如果我现在从其他代码(也可能从另一个库)调用,它会调用泛型版本还是专用化?SomeFunc<int>()

我现在有这个问题,类和函数位于两个应用程序使用的库中。一个应用程序正确使用专用化,而另一个应用程序使用泛型表单(这会导致以后的运行时问题)。为什么会有这种差异?这可能与链接器选项等有关吗?这是在 Linux 上,带有 g++ 4.1.2。

C Linux 模板 链接器 G++

评论


答:

0赞 Brandon 9/12/2008 #1

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

9赞 Serge 9/12/2008 #2

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

我的意思是fileA.h中的某个地方

template<> SomeFunc<int>();

如果不是,那可能就是原因。

0赞 oliver 9/12/2008 #3

布兰登:我就是这么想的——专用函数永远不应该被调用。我提到的第二个应用程序也是如此。但是,第一个应用会清楚地调用专用窗体,即使未在头文件中声明专用化!

我主要在这里寻求启蒙:-)因为第一个应用程序是单元测试,不幸的是,有一个错误没有出现在测试中,而是出现在真正的应用程序中......

(PS:我已经修复了这个特定的错误,确实通过在标题中声明专业化;但是可能还隐藏了哪些其他类似的错误?

2赞 Konrad Rudolph 9/12/2008 #4

根据规范,您的专用函数模板永远不应该在外部调用,除非您使用模板定义,目前没有编译器(Comeau 除外)支持(或者计划在可预见的未来)。fileA.Cexport

另一方面,一旦实例化了函数模板,编译器就会看到一个不再是模板的函数。GCC 可以在不同的编译器单元中重用此定义,因为标准规定,对于一组给定的类型参数 [temp.spec],每个模板只能实例化一次。不过,由于模板未导出,因此应仅限于编译单元。

我相信 GCC 在跨编译单元共享其实例化模板列表时可能会在这里暴露一个错误。通常,这是一个合理的优化,但它应该考虑函数专用化,但它似乎没有正确执行。

1赞 Mark Ransom 9/13/2008 #5

Microsoft C++ 年,我做了一个内联函数的实验。我想知道如果我在不同的源代码中定义一个函数的不兼容版本会发生什么。我得到了不同的结果,具体取决于我使用的是调试版本还是发布版本。在 Debug 中,编译器拒绝内联任何内容,并且无论源中的范围是什么,链接器都会链接相同版本的函数。在 Release 中,编译器内联了当时定义的任何版本,并且您获得了函数的不同版本。

在这两种情况下,都没有任何警告。我有点怀疑这一点,这就是我做这个实验的原因。

我假设模板函数的行为与其他编译器相同。

23赞 Anthony Williams 9/13/2008 #6

对在调用点不可见的模板进行专用化是错误的。不幸的是,编译器不需要诊断此错误,然后可以对您的代码执行他们喜欢的操作(在标准中,它是“格式错误,无需诊断”)。

从技术上讲,您需要在头文件中定义专用化,但几乎每个编译器都会像您预期的那样处理此问题:这在 C++11 中通过新的“外部模板”工具修复:

extern template<> SomeFunc<int>();

这明确声明特定的专业化是在其他地方定义的。许多编译器已经支持这一点,有些编译器有 .extern

0赞 Konrad Rudolph 9/13/2008 #7

@[安东尼-威廉姆斯],

您确定没有将模板声明与实例化混淆吗?据我所知,只能用于显式实例化,而不能用于专用化(这意味着隐式实例化)。[temp.expl.spec] 没有提到关键字:externextern templateextern templateextern

显式专业化
< >声明
template

3赞 Aaron 4/25/2009 #8

我在 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";
    }

1赞 CarLuva 9/11/2013 #9

正如 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>();
}

文件A.C:

#include "fileA.h"

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