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

发布于 2024-08-10 13:21:55 字数 679 浏览 2 评论 0原文

始终考虑到包含我的模板类的以下标头至少包含在两个 .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)。我理解这一点是因为据我所知,完整的模板专业化与简单的方法定义相同。

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

谢谢!

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

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.

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).

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

故人的歌 2024-08-17 13:21:55

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

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上检查过

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:

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

黑寡妇 2024-08-17 13:21:55

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

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.

小巷里的女流氓 2024-08-17 13:21:55

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

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

睫毛溺水了 2024-08-17 13:21:55

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

不过,在您的评论中,您似乎认为 inline 关键字意味着包含所有内容的函数始终被内联,但据我所知,这实际上很大程度上取决于您的编译器优化。

引用自 C++ 常见问题解答

有多种方法可以指定函数是内联的,其中一些
其中涉及 inline 关键字,其他则不涉及。不管你如何
将一个函数指定为内联函数,这是编译器的要求
允许忽略:编译器可能会内联展开部分、全部或无
调用指定为内联函数的位置。 (不
如果这看起来非常模糊,你会感到沮丧。的灵活性
上面实际上是一个巨大的优势:它让编译器可以处理大的
功能与小功能不同,而且它让编译器
如果选择正确的编译器,生成易于调试的代码
选项。)

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

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

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.

Quoting from the 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.)

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

凉城 2024-08-17 13:21:55

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

我的情况与OP的情况有点不同(但我认为足够相似,可以留下这个答案)。基本上,我使用的是第三方库,其中包含定义“状态类型”的所有不同类型的类。这些类型的核心只是枚举,但这些类都继承自公共(抽象)父级,并提供不同的实用函数,例如运算符重载和静态 toString(enum type) 函数。每个状态enum 彼此不同并且互不相关。例如,一个enum具有字段NORMAL、DEGRADED、INOPERABLE,另一个具有AVAILBLE、PENDING、MISSING等。我的软件负责管理不同组件的不同类型的状态。我想对这些 enum 类使用 toString 函数,但由于它们是抽象的,我无法直接实例化它们。我本可以扩展我想要使用的每个类,但最终我决定创建一个 template 类,其中 typename 将是任何具体状态 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 方法添加几行,然后我就可以使用已经定义的库< code>toString 方法,无需自己实现它,也无需扩展我想要使用的每个 enum 类。

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.

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 enum is different from one another and unrelated. For example, one enum has 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 toString functions for these enum classes, 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 template class, where the typename would be whatever concrete status enum I 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 enum class 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 toString functions 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.

This is a (greatly simplified) header file for the enums. In reality, each enum class 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:

// 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 toString method in the .cpp file, and I can use the libraries already-defined toString method without implementing it myself and without extending each enum class I want to use.

给我一枪 2024-08-17 13:21:55

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

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

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

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

"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..."

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文