15

当我在模板类中专门化一个(静态)成员函数/常量时,我​​对声明的去向感到困惑。

这是我要做什么的示例 - 直接来自IBM 关于模板专业化的参考

===IBM 会员专业化示例===

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

int main() {
   X<char*> a, b;
   X<float> c;
   c.f(10); // X<float>::v now set to 20
}

问题是,如何将其划分为 header/cpp 文件?通用实现显然在标题中,但专业化呢?

它不能放在头文件中,因为它是具体的,导致多重定义。但是,如果它进入 .cpp 文件,调用 X::f() 的代码是否知道特化,或者它可能依赖于通用 X::f()?

到目前为止,我只在 .cpp 中获得了专业化,在标题中没有声明。我在编译甚至运行我的代码时都没有遇到问题(在 gcc 上,目前不记得版本),并且它的行为符合预期 - 识别专业化。但是 A)我不确定这是正确的,我想知道是什么,并且 B)我的 Doxygen 文档出现问题并且非常具有误导性(稍后会详细介绍

对我来说最自然的事情是这样的,在标头中声明专业化并在 .cpp 中定义它:

===XClass.hpp===

#ifndef XCLASS_HPP
#define XCLASS_HPP

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

/* declaration of specialized functions */
template<> char* X<char*>::v;
template<> void X<float>::f(float arg);

#endif

===XClass.cpp===

#include <XClass.hpp>

/* concrete implementation of specialized functions */
template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

...但我不知道这是否正确。有任何想法吗?

4

4 回答 4

11

通常,您只需inline在标题中定义专业化,就像直接说的那样。

如果您担心编译时间或代码膨胀,您可以在单独的翻译单元中定义专业化:

// x.h:
template<class T> struct X {
    void f() {}
}

// declare specialization X<int>::f() to exist somewhere: 
template<> void X<int>::f();

// translation unit with definition for X<int>::f():
#include "x.h"
template<> void X<int>::f() {
    // ...
}

所以是的,你的方法看起来不错。请注意,您只能在完全专业化的情况下执行此操作,因此执行此操作通常不切实际。

有关详细信息,请参阅例如Comeaus 模板常见问题解答

于 2010-03-23T12:46:57.417 回答
4

将它们全部放在一个hpp文件中。进行专业化和您在类之外定义的任何内容inline——这将处理多个定义。

于 2010-03-23T08:34:11.827 回答
1

要回答您的问题之一:is code which calls X::f() aware of the specialization, or might it rely on the generic X::f()?

如果编译器看到一个符合其要求的定义,那么它将使用它。否则它将生成正常的函数调用。

在您的第一个代码片段中,您为 提供了一个通用定义X<T>::f(T arg),因此编译器将为Tfloat.

如果您要省略泛型定义,那么编译器会生成调用,比如说,X<double>::f(double)链接器会搜索可能以链接器错误结尾的定义。

总结一下:您可以在标题中包含所有内容,因为作为模板您不会获得多个定义。如果您只有声明,您将需要其他地方的定义以供链接器稍后查找。

于 2010-03-23T11:07:44.633 回答
0

改为使用template void X<float>::f(float arg) { v = arg * 2; }

于 2021-12-31T14:10:44.017 回答