C++ 模板类成员专门化的声明

C++ 模板类成员专门化的声明,c++,templates,declaration,template-specialization,C++,Templates,Declaration,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(

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

下面是我该做什么的一个例子——你直接从以下方面思考:

==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
}
模板类X{
公众:
静态tv;
静态空隙f(T);
};
模板tx::v=0;
模板void X::f(T arg){v=arg;}
模板char*X::v=“Hello”;
模板void X::f(float arg){v=arg*2;}
int main(){
xa,b;
xc;
c、 f(10);//X::v现在设置为20
}
问题是,如何将其划分为头文件/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
\ifndef XCLASS\u水电站
#定义XCLASS_水电站
模板类X{
公众:
静态tv;
静态空隙f(T);
};
模板tx::v=0;
模板void X::f(T arg){v=arg;}
/*专门职能宣言*/
模板字符*X::v;
模板void X::f(浮动参数);
#恩迪夫
==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; }
#包括
/*专门职能的具体实施*/
模板char*X::v=“Hello”;
模板void X::f(float arg){v=arg*2;}

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

把它们都放在
hpp
文件中。将专门化和您在类之外定义的任何内容
内联
——这将处理多个定义

回答您的一个问题:
调用X::f()的代码是否知道专门化,或者它是否依赖于通用的X::f()?

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

在第一个代码段中,您为
X::f(T arg)
提供了一个通用定义,因此编译器将为除
float
之外的任何
T
实例化该定义

如果省略泛型定义,那么编译器将生成调用,例如,
X::f(double)
,链接器将搜索可能以链接器错误结束的定义


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

通常您只需在标题中定义专门化
inline
,正如dirkgenty所说

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

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

// declare specialization X<int>::f() to exist somewhere: 
template<> void X<int>::f();
//x.h:
模板结构X{
void f(){}
}
//声明专门化X::f()存在于某个位置:
模板void X::f();

//具有X::f()定义的翻译单元:
#包括“x.h”
模板void X::f(){
// ...
}
是的,你的方法看起来不错。请注意,您只能在完全专业化的情况下执行此操作,因此执行此操作通常是不切实际的


有关详细信息,请参见,例如。但这不适用于静态变量-仅适用于函数。除此之外,难道没有更优雅/标准的方法来处理这个问题吗?模板类成员专门化是模板编程的主要部分。当然,“你把声明放在哪里”比“声明内联并愚弄编译器”有一个更直截了当的答案?@Ziv:我原以为静态变量在头文件中不起作用,但我在VS2008中尝试过,它似乎起了作用。专门化可以在源文件中定义,也可以在头文件中内联定义,但必须在标题中声明。@quamrana:我不明白你认为什么不起作用。“static inline int X=1;”是(据我所知)与Dirkgente的答案不符的地方。@Mike:你的评论听起来像是我要找的!我的带有XClass.hpp/XClass.cpp标题的代码片段是正确的声明方式吗?我的问题是另一种方式:如果/在哪里/如何放置专用声明。模板专门化定义不能放在标题中(除非直接建议内联)。我如何确保X::f()调用了专门化,并且没有错误地调用通用定义?@Ziv:我的实验与我的预期相反,说所有东西都可以放在标题中,静态专门化等等。这很奇怪。我试过了,得到了一个“乘法定义…”错误。耶!出于好奇的问题:为什么偏好内联?这似乎是一个奇怪的“要求”——默认情况下,模板专门化是内联的。我问这一切只是因为你暗示这是你“通常”会做的,我不明白为什么我通常会这么做,就像我通常会内联声明任何其他函数一样。这是关于将它们放在标题中的偏好,
inline
是一个副作用。少写,多优化机会,忘记一个声明不会出错。此外,您始终需要完全专门化,对于大多数模板,这意味着为使用它的每种类型组合添加一个显式声明和实例化。有点挫败了
// translation unit with definition for X<int>::f():
#include "x.h"
template<> void X<int>::f() {
    // ...
}