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

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'

Я бы предпочел, чтобы инициализация была специализированной, если это возможно, поскольку массив содержит некоторые данные, специфичные для типа.


person kaspy    schedule 26.02.2010    source источник
comment
Разве B‹A, 1›::a не будет иметь только 1 элемент? Итак, ссылаясь на B‹A, 1›::a[1], не выходите ли вы за пределы массива?   -  person Dathan    schedule 26.02.2010
comment
Я не вижу, чтобы typename A делал что-либо там.   -  person Mike DeSimone    schedule 26.02.2010


Ответы (3)


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

Ограничение этого на «для которых некоторые параметры шаблона не указаны» означает, что нам можно делать следующее, помещая его в заголовок (таким образом, возможно, имея несколько определений этой специализации):

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

В вашем случае у вас указаны все параметры, что делает его не охватываемым одним правилом определения для разрешения нескольких определений.

person Johannes Schaub - litb    schedule 26.02.2010

Вам нужно фактически присвоить ему значение.

template<> int B<A, 1>::a[1] = {0};
person kennytm    schedule 26.02.2010
comment
Разве это не должно быть template‹› int B‹A, 1›::a = {0}? - person Dathan; 26.02.2010
comment
@Датан: Почему? Ваш код error: conflicting declaration ‘int B<A, 1>::a’. - person kennytm; 26.02.2010
comment
@Dathan, это объявление будет специализировать целочисленный член, а не массив. - person Johannes Schaub - litb; 26.02.2010

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

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;
}
person neverlord    schedule 26.02.2010