模板类成员专业化声明

时间:2010-03-23 08:28:59

标签: c++ templates declaration template-specialization

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

以下是我该怎么做的示例 - 直接从IBM's reference on template specialization开始:

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

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

3 个答案:

答案 0 :(得分:9)

通常你只是在标题中定义专业化inline,就像dirkgently所说。

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

// 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 template FAQ

答案 1 :(得分:4)

将它们全部放在hpp文件中。制作专业化以及您在课程inline之外定义的任何内容 - 这将负责多个定义。

答案 2 :(得分: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)提供了一个通用定义,因此编译器将为除T之外的任何float实例化。

如果你要省略通用定义,那么编译器会生成对X<double>::f(double)的调用,链接器会搜索可能以链接器错误结束的定义。

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