【问题标题】:Specialisations of class template members类模板成员的特化
【发布时间】:2014-09-10 06:31:59
【问题描述】:

我有一个标题,其中包含一个带有模板成员的类和该成员的一些特化:

#include <iostream>
class Foo {
public:
    template<typename T>
    void print(const T& t) {
        std::cout << t << std::endl;
    }
};

template<>
void Foo::print<int>(const int& t) {
    std::cout << t << std::endl;
}

此标头包含在多个源文件中。

如果我将专业化放在类定义中,那么 GCC 会抱怨:

error: explicit specialization in non-namespace scope ‘class Foo’

但是如果我将特化移到类定义之外,VC++ 就会抱怨多重定义的符号。

两个编译器都会满意的正确方法是什么?

【问题讨论】:

    标签: c++ templates template-specialization


    【解决方案1】:

    显式专门化的模板函数不再是模板(它不再依赖于任何模板参数)。因此,它作为普通函数遵守单一定义规则。这意味着明确专门化的函数的定义应该在整个程序中只进行一次。 IE。您必须将定义放入实现文件(.cpp 文件)中。

    但是,您仍然必须在头文件中声明这个特化(告诉编译器它确实存在)。 IE。在你必须做的头文件中

    template<> 
    void Foo::print<int>(const int& t);
    

    (请注意,根据标准要求,它应该在命名空间范围内完成,即在类定义之外。)

    然后在一个实现文件中进行

    template<>
    void Foo::print<int>(const int& t) {
        std::cout << t << std::endl;
    }
    

    附:当然,就像任何其他函数一样,您可以将其声明为inline,并将定义保留在头文件中。

    【讨论】:

    • 嗯,很公平,我猜。但是如果它只是一个普通的成员函数,那为什么G++不让你在类里面声明呢?
    • @Tom 因为成员函数的显式特化必须在命名空间范围内,无论它们是否是成员。
    • @Tom:简短回答:因为语言标准是这样说的。显式特化必须在命名空间范围内声明。长答案:我不知道这背后的理由。我自己一直想知道为什么在类范围内允许部分特化,但不允许显式特化。
    【解决方案2】:

    标准说(§14.7.3/12):

    函数模板的显式特化只有在它 使用 inline 说明符声明或定义为已删除,并且 与其函数模板是否内联无关。

    ODR 将函数模板的特化视为普通函数(无特化)。 如果您在标头中定义特化,则必须使用 inline 说明符声明它,以便多个翻译单元可以包含它。

    如果你想在源文件中定义它,你必须在头文件中声明特化(在命名空间范围内):

    template<>
    void Foo::print<int>(const int&);
    

    并将定义放在源文件中

    template<>
    void Foo::print<int>(const int& t)
    {
        std::cout << t << std::endl;
    }
    

    ,就像你习惯使用普通函数一样。

    【讨论】:

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