专门化模板类的静态成员初始化

19
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'

如果可能的话,我更倾向于保持初始化专业化,因为数组保存了一些特定类型的数据。


1
B<A, 1>::a难道只有一个元素吗?所以通过引用B<A, 1>::a[1],你不是在数组的边界之外进行索引吗? - Dathan
我在那里看不到 typename A 做任何事情。 - Mike DeSimone
3个回答

41
对于静态成员的特化,如果您不初始化该成员,则会将其视为一个专门的声明,仅表示“哦,不要从主模板实例化该成员,因为在其他地方有一个专门的定义”。应当指出,定义应该出现在.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

An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [Note: there is no syntax for the definition of a static data member of a template that requires default initialization.

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

This is a declaration regardless of whether X can be default initialized (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)或一些模板参数未指定的模板特化(14.7,14.5.4)的定义[...]

这个限制是针对“一些模板参数未指定”的,这意味着我们允许执行以下操作,将其放入标头中(因此可能有多个此专业化的定义):

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

在您的情况下,您已经指定了所有参数,因此不受允许多个定义的一个定义规则的覆盖。

2

你需要真正为它赋值。

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

1
难道不应该是 template<> int B<A, 1>::a = {0} 吗? - Dathan
@Dathan:为什么?你的代码是error: conflicting declaration ‘int B<A, 1>::a’ - kennytm
1
@Dathan,那个声明会特化一个整数成员,而不是一个数组。 - Johannes Schaub - litb

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接