关于linux:C函数模板专业化的可见性

关于linux:C函数模板专业化的可见性

Visibility of template specialization of C++ function

假设我有fileA.h,它使用模板函数SomeFunc<T>()声明了一个类classA。此功能直接在头文件中实现(与模板功能一样)。现在,我在fileA.C(即不在头文件中)中添加了SomeFunc()的专用实现(例如对于SomeFunc<int>())。

如果我现在从其他代码(也许也从另一个库)调用SomeFunc<int>(),它将调用通用版本还是专业化版本?

我现在遇到这个问题,类和函数都存在于两个应用程序使用的库中。一个应用程序正确使用专业化,而另一个应用程序使用通用形式(稍后会导致运行时问题)。为什么会有所不同?可能与链接器选项等有关吗?这是在Linux上,带有g 4.1.2。


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

从技术上讲,您需要在头文件中定义特殊化,但是几乎每个编译器都会按照您的期望进行处理:这在C 11中已通过新的"外部模板"功能进行了修复:

1
extern template<> SomeFunc<int>();

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


您是否已将带有参数的原型添加到头文件中?

我的意思是文件A.h中有某处

1
template<> SomeFunc<int>();

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


我在gcc4上遇到了同样的问题,这是我如何解决的。这比我之前的评论使我相信的解决方案更简单。先前的帖子想法是正确的,但是它们的语法对我不起作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    ----------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";
    }

根据规范,切勿在fileA.C之外调用专用功能模板,除非您export模板定义,当前没有编译器(除Comeau外)支持(或在可预见的将来计划了该模板定义)。铅>

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

我相信GCC在跨编译单元共享其实例化模板列表时可能会暴露一个错误。通常,这是一个合理的优化,但是应该考虑功能专业化,而这似乎并不能正确地完成。


正如Anthony Williams所说,extern template构造是执行此操作的正确方法,但是由于他的示例代码不完整并且有多个语法错误,因此这是一个完整的解决方案。

fileA.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

1
2
3
#include"fileA.h"

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

在Microsoft C中,我对内联函数进行了实验。我想知道如果我在其他来源中定义了不兼容版本的函数会发生什么。根据使用的是Debug版本还是Release版本,我得到不同的结果。在Debug中,编译器拒绝内联任何内容,并且无论源代码中的作用域如何,链接器都在链接相同版本的函数。在Release中,编译器内联了当时定义的任何版本,并且您获得了该函数的不同版本。

在任何情况下都没有任何警告。我有点怀疑,这就是我进行实验的原因。

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


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

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

explicit-specialization:
template < > declaration


布兰登:这就是我的想法-不应调用专用功能。对于我提到的第二个应用程序,这是正确的。但是,即使未在头文件中声明特殊化,第一个应用程序也显然会调用特殊化形式!

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

(PS:我已经通过在标头中声明特殊性来修复此特定的错误;但是还有哪些其他类似的错误可能仍被隐藏?)


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


推荐阅读