Статическая инициализация члена для специализированного класса шаблона

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

Для статических членских специализаций, если вы не инициализируете член, это берется как объявление специализации, которое просто говорит: "О, не создавайте экземпляр элемента из основного шаблона, потому что есть специализированное определение где-то в другом месте". Следует отметить, что определение должно появиться в файле .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();

С++ 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) или специализированная специализация шаблона, для которой некоторые параметры шаблона не указаны (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};

Ответ 3

Он не ссылается, потому что вы не определяете значение для своего статического члена.

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

Edit:

Btw: я бы всегда предпочитал использовать 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;
}