【问题标题】:Declaration of template class member specialization模板类成员特化声明
【发布时间】:2011-01-30 16:53:46
【问题描述】:

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

这是我该做什么的一个例子——直接来自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 文档出来时很不稳定并且非常具有误导性(稍后将在 a moment 中详细介绍问题)。

在我看来最自然的事情是这样的,在标头中声明专业化并在 .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; }

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

【问题讨论】:

    标签: c++ templates declaration template-specialization


    【解决方案1】:

    通常您只需在标题中定义专业化 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 template FAQ.

    【讨论】:

    • 问题,出于好奇:为什么偏爱内联?强加似乎是一个奇怪的“要求” - “默认情况下”模板专业化将被内联。我问这一切只是因为你暗示这就是你“通常”会做的事情,而且我不明白为什么我通常想要这样做,而不是我想要通常在内联声明任何其他函数。跨度>
    • 关于将它们放在标题中的偏好,inline 是一个副作用。少写,更多优化机会,忘记一个声明就不会出错。此外,您总是需要完全专业化,对于大多数模板来说,这意味着为使用的每种类型组合添加一个显式声明和实例化。有点违背通用性的意图,不是吗?
    • 嗯,当然,我只是在谈论需要完全专业化的情况,否则显然是一个完全不同的故事......然后翻转问题:为什么不一样优势导致我们通常更喜欢内联编写所有函数?
    • 当编译时间或代码膨胀成为一个问题时,我想不出更多。
    • 嗨 Georg,我在 Visual Studio 2015 中遇到了这个问题:developercommunity.visualstudio.com/content/problem/143189/…,你知道现在有没有解决方案?
    【解决方案2】:

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

    【讨论】:

    • 有趣。但这不适用于静态变量 - 仅适用于函数。除此之外 - 没有更优雅/标准的方式来处理这个问题吗?模板类成员特化是模板编程的主要部分。当然,“你把声明放在哪里”比“声明内联并欺骗编译器”有更直接的答案?
    • @Ziv:我认为静态变量在标题中不起作用,但我在 VS2008 中尝试过,它似乎有效。
    • 特化可以在源文件中定义,也可以在头文件中内联,但必须在头文件中声明。
    • @quamrana:我不明白你认为什么是行不通的。 “静态内联 int X = 1;”是什么(据我所知)不适用于 dirkgently 的答案。
    • @Mike:你的评论听起来像我要找的!我的带有 XClass.hpp/XClass.cpp 标题的代码 sn-p 是声明它们的正确方法吗?
    【解决方案3】:

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

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

    在您的第一个代码 sn-p 中,您为X&lt;T&gt;::f(T arg) 提供了一个通用定义,因此编译器将为除float 之外的任何T 实例化它。

    如果您要省略通用定义,那么编译器会生成对 X&lt;double&gt;::f(double) 的调用,并且链接器会搜索可能以链接器错误结尾的定义。

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

    【讨论】:

    • 我的问题是相反的:如果/在哪里/如何放置专门的声明。模板专业化定义不能进入标题(除非内联,如直接建议的那样)。如何确保 X::f() 调用专业化,并且不会错误地调用泛型定义?
    • @Ziv:我的实验,出乎我的意料,说一切可以进入标题,静态特化等等。
    • 这很奇怪。我试过了,我得到了一个“多重定义......”错误。耶编译器一致性!
    【解决方案4】:

    改用template void X&lt;float&gt;::f(float arg) { v = arg * 2; }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多