【问题标题】:Template method specialization linking error模板方法特化链接错误
【发布时间】:2012-12-14 05:49:11
【问题描述】:

考虑以下头文件和源文件:

// main.cpp
#include "myClass.h"

int main()
{
  MyClass m;
  m.foo<double>();
  m.foo<float>();
}

// myClass.h
#pragma once

#include <iostream>

using namespace std;

class MyClass
{
public:

  template <typename T>
  void foo()
  {
    cout << "Template function<T> called" << endl;
  }

  template <>
  void foo<int>()
  {
    cout << "Template function<int> called" << endl;
  }

  template <>
  void foo<float>();

};

// myClass.cpp
#include "myClass.h"

template <>
void MyClass::foo<float>()
{
  cout << "Template function<float> called" << endl;
}

foo&lt;float&gt; 专业化出现链接错误。如果我将专业化的定义放在头文件中,那么一切都会按预期进行。

我认为原因可能是该方法没有显式实例化(尽管template class 的完全特化不需要显式实例化来正确链接)。如果我尝试显式实例化该方法,则会收到此错误:

error C3416: 'MyClass::foo' : 显式特化可能没有显式实例化

所以问题是:

  • 有没有办法在cpp 文件中定义特化并正确链接?
  • 如果不是,为什么不呢?我可以显式实例化模板方法 不专业就好了。为什么完全专业化不一样?

【问题讨论】:

  • @billz:这不是重复的(或者我不擅长搜索......但我和我的同事搜索了很长时间)。关于类专业化和template function 专业化有很多问题,但当类是非模板(这有关系吗?)并且template method 是完整专业化时则不会。
  • @billz:关于您的重复链接,这是一个关于使用模板链接错误的一般问题。我已经知道显式实例化类模板和模板方法的技巧。这里的问题是fulltemplate method 的特化。
  • 显示我的工具链是多么陈旧(Apple LLVM 4.1,supposed 兼容 C++11),因为此代码中的头文件本身不会t 为我编译;在第一个类内特化时出现 ("Explicit specialization of 'foo' in class scope") 的错误。
  • @WhozCraig Gcc 4.7.2 也不会编译,但 VS2012 可以编译
  • @didierc:绝对可以将模板的定义放在单独的翻译单元中。您需要做的就是显式地实例化它:stackoverflow.com/a/4933205/368599 顺便说一句,这让我感到困惑,因此提出了这个问题。为什么完全专业化的模板方法不受这些规则的约束?

标签: c++ templates c++03


【解决方案1】:

虽然 WhozCraig 的回答(现已删除)提供了解决您问题的正确代码,但这里有一些直接回答您的问题,包括代码中的 cmets:

  1. foo&lt;int&gt;()foo&lt;float&gt;() 是成员模板的显式特化。那些不能出现在它们所属的类的定义中。标准说:

    (§14.7.3/3) [...] 类或类模板的定义应在类或类模板的成员模板的显式特化声明之前。 [...]

    所以你必须把它们放在类定义之后。

  2. 对于foo&lt;int&gt;,在头文件中完全定义,这意味着你必须在定义前加上inline这个词;否则,如果头文件包含在多个翻译单元中,您将遇到链接器问题。

  3. foo&lt;float&gt;() 的特化在一个单独的文件中定义,该文件稍后链接到main.cpp。这是可能的,但它要求在头文件中给出它的声明(你已经这样做了,但你必须在类定义之外这样做):

      template <>
      void MyClass::foo<float>();
    

    这是必需的,因为标准中的另一个声明:

    (§14.7.3/6) 如果模板、成员模板或类模板的成员被显式特化,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化, 在出现这种使用的每个翻译单元中;不需要诊断。 [...]

  4. 由于所有特化都是显式(即完全特化,用你的话来说),因此不需要显式实例化,但它们是可能的(第 14.7.2 节)。您可以将它们放在 .cpp 文件的末尾,语法为:

    template void MyClass::foo<float>();
    template void MyClass::foo<int>();
    

    同样,这仅对那些没有有自己显式特化的类型真正有用。

因此,头文件和实现文件的正确代码如下所示:

.h 文件:

class MyClass
{
public:
  template <typename T> void foo()
  { cout << "Template function<T> called" << endl; }
};

template <> inline void MyClass::foo<int>()
{ cout << "Template function<int> called" << endl; }

template <> void MyClass::foo<float>();

.cpp:

#include "myClass.h"

template <> void MyClass::foo<float>()
{ cout << "Template function<float> called" << endl; }

/* This is unnecessary for float, but may be useful for
   types that do not have their own explicit specializations: */
template void MyClass::foo<float>();

【讨论】:

  • +1。 谢谢你。 #4是我完全放屁的部分。我无法理解需要处理给定的先决条件。我很确定其余的都很好。这是一个可靠的答案,标准引用使它更适合这个问题,所以我将完全放弃我的答案并赞成这个。在你闲暇时从身体上删除我的参考。我很高兴我没有考虑过深层次的问题。
  • 很好的答案,解释了一切!谢谢! (+1)
  • 在倒数第二个代码示例中,对于类主体之外的代码示例,不应该是 MyClass::f... 而不是 f 吗?
  • @0x499602D2 绝对——感谢您指出这一点。我现在已经更正了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
  • 2020-12-24
  • 2012-08-13
相关资源
最近更新 更多