模板化类中单个方法的模板特化

时间:2009-11-12 16:31:20

标签: c++ visual-studio-2008 templates specialization

始终考虑包含我的模板类的以下标头包含在至少两个.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?代码不应该在每次使用时都重复。我搜索了Google,在SO中阅读了一些问题并尝试了许多建议的解决方案但没有成功构建(至少在VS 2008中没有)。

谢谢!

6 个答案:

答案 0 :(得分:63)

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

如果您想以任何理由删除内联,则maxim1000的解决方案完全有效。

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

C++ FAQ

引用
  

有几种方法可以指定函数是内联函数,有些是   其中涉及内联关键字,其他则不涉及。不管你怎么样   将函数指定为内联函数,它是编译器的请求   允许忽略:编译器可能内联扩展some,all或none   您调用指定为内联函数的地方。 (别   如果那似乎毫无希望地含糊不清,就会气馁。灵活性   上面实际上是一个巨大的优势:它让编译器处理大   功能与小功能不同,加上它让编译器   如果选择正确的编译器,则生成易于调试的代码   选项)。

因此,除非您知道该函数实际上会使您的可执行文件膨胀,或者除非您出于其他原因将其从模板定义标题中删除,否则您实际上可以将其保留在没有任何损害的位置

答案 4 :(得分:1)

我想补充一点,如果您还打算在头文件中保留特殊性,那么仍然有充分的理由将inline关键字保留在那里。

  

“直观上讲,当您完全专门化某些东西时,它不再依赖于模板参数-因此,除非您将专门化内容内联,否则需要将其放入.cpp文件而不是.h中,否则最终违反了一个定义规则...”

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

答案 5 :(得分:0)

这有点旧,但是我想我把它留在这里,以防其他人受到帮助。我一直在关注模板专业化,这导致了我的到来,虽然@ maxim1000的答案是正确的,并最终帮助我弄清了我的问题,但我认为这并不是很清楚。

我的情况与OP略有不同(但我认为足以回答这个问题)。基本上,我使用的是第三方库,其中包含定义“状态类型”的所有不同种类的类。这些类型的核心只是enum,但是这些类都继承自公共(抽象)父类,并提供不同的实用程序功能,例如运算符重载和static toString(enum type)函数。每个状态enum互不相同且互不相关。例如,一个enum具有字段NORMAL, DEGRADED, INOPERABLE,另一个具有AVAILBLE, PENDING, MISSING等。我的软件负责管理不同组件的不同类型的状态。结果是,我想为这些toString类使用enum函数,但是由于它们是抽象的,因此无法直接实例化它们。我本可以扩展我想使用的每个类,但是最终我决定创建一个template类,其中typename将是我关心的任何具体状态enum。关于该决定,可能会有一些争论,但我觉得这比用我自己的自定义对象扩展每个抽象enum类并实现抽象功能要少得多。当然,在我的代码中,我只想能够调用.toString(enum type)并使其打印该enum的字符串表示形式。由于所有enum都不相关,因此它们每个人都有自己的toString函数(根据我的研究,这些函数必须使用模板专门化来调用)。那把我引到了这里。以下是我为使此工作正常进行所必须执行的操作的MCVE。实际上,我的解决方案与@ maxim1000的解决方案有点不同。

这是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

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