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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1723537/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:00:50  来源:igfitidea点击:

Template specialization of a single method from a templated class

c++visual-studio-2008templatesspecialization

提问by Chuim

Always considering that the following header, containing my templated class, is included in at least two .CPPfiles, this code compiles correctly:

始终考虑到包含我的模板化类的以下头.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
}

But note the inline in the specialization method. It is required to avoid a linker error (in VS2008 is LNK2005) due to the method being defined more then once. I understand this because AFAIK a full template specialization is the same as a simple method definition.

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

So, how do I remove that inline? The code should not be duplicated in every use of it. I've searched Google, read some questions here in SO and tried many of the suggested solutions but none successfully built (at least not in VS 2008).

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

Thanks!

谢谢!

采纳答案by maxim1000

As with simple functions you can use declaration and implementation. Put in your header declaration:

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

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

and put implementation into one of your cpp-files:

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

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

Don't forget to remove inline (I forgot and thought this solution will not work :) ). Checked on VC++2005

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

回答by BostonLogan

You need to move specialization definition to CPP file. Specialization of member function of template class is allowed even if function is not declared as template.

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

回答by Martin York

There is no reason to remove the keyword inline.
It does not change the meaning of the code in anyway.

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

回答by Triskeldeian

If you want to remove the inline for whatever reason the solution of maxim1000 is perfectly valid.

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

In your comment, though, it seems you believe that the inline keyword means that the function with all his contents gets always inlined but AFAIK that is actually very much dependent on your compiler optimization.

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

Quoting from the C++ FAQ

引用C++ FAQ

There are several ways to designate that a function is inline, some of which involve the inline keyword, others do not. No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: the compiler might inline-expand some, all, or none of the places where you call a function designated as inline. (Don't get discouraged if that seems hopelessly vague. The flexibility of the above is actually a huge advantage: it lets the compiler treat large functions differently from small ones, plus it lets the compiler generate code that is easy to debug if you select the right compiler options.)

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

So, unless you know that that function will actually bloat your executable or unless you want to remove it from the template definition header for other reasons, you can actually leave it where it is without any harm

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

回答by Jordan

I'd like to add that there is still a good reason to keep the inlinekeyword there if you intend to leave also the specialization in the header file.

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

"Intuitively, when you fully specialize something, it doesn't depend on a template parameter any more -- so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule..."

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

Reference: https://stackoverflow.com/a/4445772/1294184

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

回答by yano

This is a little OT, but I thought I'd leave this here in case it helps someone else. I was googling about template specialization which led me here, and while @maxim1000's answer is correct and ultimately helped me figure my problems out, I didn't think it was abundantly clear.

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

My situation is a little different (but similar enough to leave this answer I think) than the OP's. Basically, I'm using a third party library with all different kinds of classes that define "status types". The heart of these types are simply enums, but the classes all inherit from a common (abstract) parent and provide different utility functions, such as operator overloading and a static toString(enum type)function. Each status enumis different from one another and unrelated. For example, one enumhas the fields NORMAL, DEGRADED, INOPERABLE, another has AVAILBLE, PENDING, MISSING, etc. My software is in charge of managing different types of statuses for different components. It came about that I wanted to utilize the toStringfunctions for these enumclasses, but since they're abstract I couldn't instantiate them directly. I could have extended each class I wanted to use, but ultimately I decided to create a templateclass, where the typenamewould be whatever concrete status enumI cared about. Probably some debate can be had about that decision, but I felt like that was a lot less work than extending each abstract enumclass with a custom one of my own and implementing the abstract functions. And of course in my code, I just wanted to be able to call .toString(enum type)and have it print the string representation of that enum. Since all the enums were entirely unrelated, they each had their own toStringfunctions that (after some research I learned) had to be called using template specialization. That led me here. Below is an MCVE of what I had to do in order to make this work correctly. And actually my solution was a bit different than @maxim1000's.

我的情况与 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 的有点不同。

This is a (greatly simplified) header file for the enums. In reality, each enumclass was defined in it's own file. This file represents the header files that are supplied to me as part of the library I am using:

这是enums的(大大简化的)头文件。实际上,每个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
  }
};

adding this line just to separate the next file into a different code block:

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

// 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()
{

}

next file

下一个文件

// 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_);
}

next file

下一个文件

// 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;
}

and this outputs:

这输出:

BEARS1
TIGERS3

No clue if this is the ideal solution to solve my problem, but it worked for me. Now, no matter how many enumeration types I end up using, all I have to do is add a few lines for the toStringmethod in the .cpp file, and I can use the libraries already-defined toStringmethod without implementing it myself and without extending each enumclass I want to use.

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