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

时间:2008-09-12 15:36:15

标签: c++ linux templates linker g++

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

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

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

9 个答案:

答案 0 :(得分:22)

错误,对于在通话点处不可见的模板具有专门性。遗憾的是,编译器不需要诊断此错误,然后可以使用您的代码执行他们喜欢的操作(在标准中它“形成错误,无需诊断”)。

从技术上讲,你需要在头文件中定义特化,但几乎每个编译器都会像你期望的那样处理它:这在C ++ 11中使用新的“extern模板”工具修复:

extern template<> SomeFunc<int>();

这明确声明特定的特化在别处定义。许多编译器已经支持这一点,有些编译器支持,有些没有extern

答案 1 :(得分:9)

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

我的意思是在fileA.h中有某处

template<> SomeFunc<int>();

如果不是那可能是原因。

答案 2 :(得分: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";
    }

答案 3 :(得分:2)

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

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

我相信GCC可能会在编译单元中共享其实例化模板列表时暴露一个错误。通常情况下,这是一个合理的优化,但它应该考虑功能特化,而这似乎没有正确。

答案 4 :(得分: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>() { ... }

答案 5 :(得分:0)

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

答案 6 :(得分:0)

布兰登:这就是我的想法 - 永远不应该调用专门的函数。对于我提到的第二个应用程序,这是正确的。但是,第一个应用程序显然会调用专用表单,即使未在头文件中声明特化!

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

(PS:我已经修复了这个特定的错误,实际上是通过声明标题中的特化;但是其他类似的错误仍然可能被隐藏?)

答案 7 :(得分:0)

在Microsoft C ++中,我使用内联函数进行了实验。我想知道如果我在不同的源中定义了不兼容的函数版本会发生什么。根据我是使用Debug构建还是Release构建,我得到了不同的结果。在Debug中,编译器拒绝内联任何内容,并且链接器链接相同版本的函数,无论源中的范围是什么。在Release中,编译器内联了当时定义的任何版本,并且您有不同版本的函数。

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

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

答案 8 :(得分:0)

@ [安东尼 - 威廉姆斯]

您确定不会将extern模板声明与extern template实例化混淆吗?根据我的看法,extern template可能用于显式实例化,而不是用于特化(这意味着隐式实例化)。 [temp.expl.spec]未提及extern关键字:

  

明确分工
  template&lt; &GT; 声明