96

始终考虑到包含我的模板类的以下标头至少包含在两个.CPP文件中,因此此代码可以正确编译:

template <class T>
class TClass 
{
public:
  void doSomething(std::vector<T> * v);
};

template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
  // Do something with a vector of a generic T
}

template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
  // Do something with a vector of int's
}

但请注意专业化方法中的内联。由于方法被定义不止一次,因此需要避免链接器错误(在 VS2008 中是 LNK2005)。我理解这一点,因为 AFAIK 完整的模板专业化与简单的方法定义相同。

那么,我该如何删除它inline?代码不应在每次使用时重复。我搜索了谷歌,在这里阅读了一些问题,并尝试了许多建议的解决方案,但没有一个成功构建(至少在 VS 2008 中没有)。

谢谢!

4

6 回答 6

73

与简单函数一样,您可以使用声明和实现。放入您的标题声明:

template <>
void TClass<int>::doSomething(std::vector<int> * v);

并将实现放入您的 cpp 文件之一:

template <>
void TClass<int>::doSomething(std::vector<int> * v) {
 // Do somtehing with a vector of int's
}

不要忘记删除内联(我忘记并认为此解决方案不起作用:))。在 VC++2005 上检查

于 2009-11-12T16:49:37.320 回答
4

您需要将专业化定义移至 CPP 文件。即使函数未声明为模板,也允许对模板类的成员函数进行特化。

于 2009-11-12T16:50:53.590 回答
3

没有理由删除关键字 inline。
无论如何,它不会改变代码的含义。

于 2009-11-12T16:55:42.203 回答
2

如果您出于某种原因想要删除内联,maxim1000 的解决方案是完全有效的。

但是,在您的评论中,您似乎认为 inline 关键字意味着具有其所有内容的函数始终被内联,但 AFAIK 实际上非常依赖于您的编译器优化。

引用C++ FAQ

有几种方法可以指定函数是内联的,其中一些涉及 inline 关键字,另一些则不涉及。无论您如何将函数指定为内联,它都是允许编译器忽略的请求:编译器可能会内联扩展您调用指定为内联的函数的部分、全部或任何位置。(如果这看起来非常模糊,请不要气馁。上面的灵活性实际上是一个巨大的优势:它允许编译器将大函数与小函数区别对待,此外,如果您选择它,它还可以让编译器生成易于调试的代码正确的编译器选项。)

因此,除非您知道该函数实际上会使您的可执行文件膨胀,或者除非您出于其他原因想要将其从模板定义标头中删除,否则您实际上可以将其留在原处而不会造成任何伤害

于 2015-07-06T10:11:54.057 回答
1

这有点过时,但我想我会把它留在这里以防它帮助别人。我在谷歌上搜索导致我来到这里的模板专业化,虽然@maxim1000 的回答是正确的,并最终帮助我解决了我的问题,但我认为它并不十分清楚。

我的情况与 OP 的情况略有不同(但我认为足以留下这个答案)。基本上,我使用的是第三方库,其中包含定义“状态类型”的所有不同类型的类。这些类型的核心只是enums,但这些类都继承自一个公共(抽象)父类,并提供不同的实用函数,例如运算符重载和static toString(enum type)函数。每种状态enum都彼此不同且不相关。例如,一个enum有 fields NORMAL, DEGRADED, INOPERABLE,另一个有AVAILBLE, PENDING, MISSING等。我的软件负责管理不同组件的不同类型的状态。我想利用这些toString功能来实现enum类,但由于它们是抽象的,我无法直接实例化它们。我本可以扩展我想使用的每个类,但最终我决定创建一个template类,typename它将是enum我关心的任何具体状态。关于这个决定可能会有一些争论,但我觉得这比enum用我自己的自定义类扩展每个抽象类并实现抽象函数要少得多。当然,在我的代码中,我只是希望能够调用.toString(enum type)并让它打印那个enum. 由于所有的enums 都是完全不相关的,所以它们都有自己的toString必须使用模板专业化调用的函数(经过我了解的一些研究)。这把我带到了这里。下面是我必须做的一个 MCVE,以使其正常工作。实际上我的解决方案与@maxim1000 的有点不同。

这是 s 的(非常简化的)头文件enum。实际上,每个enum类都在它自己的文件中定义。此文件表示作为我正在使用的库的一部分提供给我的头文件:

// file enums.h
#include <string>

class Enum1
{
public:
  enum EnumerationItem
  {
    BEARS1,
    BEARS2,
    BEARS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

class Enum2
{
public:
  enum EnumerationItem
  {
    TIGERS1,
    TIGERS2,
    TIGERS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

添加这一行只是为了将下一个文件分成不同的代码块:

// file TemplateExample.h
#include <string>

template <typename T>
class TemplateExample
{
public:
  TemplateExample(T t);
  virtual ~TemplateExample();

  // this is the function I was most concerned about. Unlike @maxim1000's
  // answer where (s)he declared it outside the class with full template
  // parameters, I was able to keep mine declared in the class just like
  // this
  std::string toString();

private:
  T type_;
};

template <typename T>
TemplateExample<T>::TemplateExample(T t)
  : type_(t)
{

}

template <typename T>
TemplateExample<T>::~TemplateExample()
{

}

下一个文件

// file TemplateExample.cpp
#include <string>

#include "enums.h"
#include "TemplateExample.h"

// for each enum type, I specify a different toString method, and the
// correct one gets called when I call it on that type.
template <>
std::string TemplateExample<Enum1::EnumerationItem>::toString()
{
  return Enum1::toString(type_);
}

template <>
std::string TemplateExample<Enum2::EnumerationItem>::toString()
{
  return Enum2::toString(type_);
}

下一个文件

// and finally, main.cpp
#include <iostream>
#include "TemplateExample.h"
#include "enums.h"

int main()
{
  TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1);
  TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3);

  std::cout << t1.toString() << std::endl;
  std::cout << t2.toString() << std::endl;

  return 0;
}

这输出:

BEARS1
TIGERS3

不知道这是否是解决我的问题的理想解决方案,但它对我有用。现在,无论我最终使用了多少枚举类型,我所要做的就是toString在 .cpp 文件中为方法添加几行,我可以使用库中已经定义的toString方法,而无需自己实现它,也无需扩展每个enum我想使用的类。

于 2019-01-12T02:50:58.027 回答
1

我想补充一点,inline如果您打算在头文件中也保留专业化,仍然有充分的理由将关键字保留在那里。

“直观地说,当你完全特化某事时,它不再依赖于模板参数——所以除非你将特化内联,否则你需要把它放在 .cpp 文件而不是 .h 文件中,否则你最终会违反单一定义规则……”

参考:https ://stackoverflow.com/a/4445772/1294184

于 2019-11-15T05:49:55.570 回答