18
class A
{
};

template <typename A, int S>
class B
{
public:
        static int a[S];

        B()
        {
                a[0] = 0;
        }
};

template<> int B<A, 1>::a[1];

int main()
{
        B<A, 1> t;
        t;
}

它在 GCC 4.1 下编译,但不链接:

static.cpp:(.text._ZN1BI1ALi1EEC1Ev[B<A, 1>::B()]+0x5): undefined reference to `B<A, 1>::a'

如果可能的话,我宁愿保持初始化专门化,因为数组包含一些特定于该类型的数据。

4

3 回答 3

40

对于静态成员特化,如果您不初始化成员,则将其视为特化声明,即“哦,不要从主模板实例化成员,因为其他地方有专门的定义”。需要说明的是,定义应该出现在一个 .cpp 文件中(否则,你会得到相反的结果:多个定义),并且没有初始化器的声明仍然应该放在头文件中。

现在正确的语法确实如下,而且不应该出现在头文件中,而是出现在.cpp文件中

template<> int B<A, 1>::a[1] = { };

以下内容仍应出现在头文件中:

template<> int B<A, 1>::a[1];

这将作为专业化声明


由此可见,您不能专门化只有默认构造函数且不可复制的成员,因为您需要以下语法:

// needs a copy constructor!
template<> Type Class<Arguments>::member = Type();

C++0x 解决了这个问题:

// doesn't anymore need a copy constructor
template<> Type Class<Arguments>::member{};

对于我们中间的标准人,以下是引述:

14.7.3/6

如果模板、成员模板或类模板的成员是显式特化的,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在每个翻译单元中出现这种使用; 不需要诊断。

14.7.3/15

如果声明包含初始化器,则模板的静态数据成员的显式特化是定义;否则,它是一个声明。[注意:对于需要默认初始化的模板的静态数据成员的定义,没有语法。

template<> X Q<int>::x;

无论 X 是否可以默认初始化(8.5),这都是一个声明。]

3.2/3

每个程序都应包含该程序中使用的每个非内联函数或对象的准确定义;无需诊断。

3.2/5

类类型(第 9 条)、枚举类型(7.2)、带有外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数模板(14.5.5)可以有多个定义、类模板的静态数据成员 (14.5.1.3)、类模板的成员函数 (14.5.1.1) 或在程序 [.. .]

将此限制为“未指定某些模板参数”意味着我们可以执行以下操作,将其放入标题中(因此可能具有此专业化的多个定义):

template<> template<typename T>
Type OuterClass<int>::InnerClass<T>::StaticMember = 0;

在您的情况下,您指定了所有参数,使其不受允许多个定义的单一定义规则的覆盖。

于 2010-02-26T15:32:32.150 回答
2

您需要为它实际分配一个值。

template<> int B<A, 1>::a[1] = {0};
于 2010-02-26T15:15:02.677 回答
1

它没有链接,因为您没有为静态成员定义值。

template<> int B<A, 1>::a[] = { 0 };

编辑:

顺便说一句:我总是更喜欢使用 boost::array 而不是原生 C 类型:

class A { };

template <typename A, std::size_t S>
class B
{
public:
    static boost::array<int, S> a;

    B() { a[0] = 0; }
};

template<>  boost::array<int, 1> B<A, 1>::a = { };

int main()
{
    B<A, 1> t;
    cout << t.a[0] << endl;
}
于 2010-02-26T15:23:32.047 回答