Подтвердить что ты не робот

Какой правильный способ специализировать шаблон при использовании "шаблона extern"?

Я надеюсь, что кто-то может указать правильный способ специализации метода в классе шаблона при использовании "extern template class" и "template class" для явного создания экземпляра с помощью gnu С++. Я попытался свести эту проблему с простейшим примером, который подражает моей реальной проблеме. По-видимому, объявление "шаблона extern" подразумевает создание шаблона, которое вызывает ошибки при специализированных методах. С учетом программы драйвера:

main.cc

#include A_H
#include <iostream>

int main()
{
    A<int> ai;
    A<long> al;

    std::cout << "ai=" << ai.get() << " al=" << al.get() << std::endl;

    return 0;
}

И следующая реализация A

хиджры

template<typename T>
struct A
{
    int get() const;
};

extern template class A<int>;
extern template class A<long>;

a.cc

#include "a.h"

template<typename T>
int A<T>::get() const
{
    return 0;
}

template<>
int A<long>::get() const
{
    return 1;
}

template class A<int>;
template class A<long>;

Я получаю следующую ошибку при компиляции с помощью: g++ 4.1.2 или 4.4.4

 % g++ -Wall -g -D'A_H="a.h"' a.cc main.cc          
a.cc:10: error: specialization of 'int A<T>::get() const [with T = long int]' after instantiation
 %

Если я прокомментирую две строки "extern template" в a.h, вещи компилируются и работают как ожидалось с обоими компиляторами. Я предполагаю, что в зависимости от существования явного экземпляра в отсутствие "шаблона extern" это неопределенное поведение даже в С++ 0x, в противном случае, что точка С++ 0x, добавляющая "шаблон extern"?

Если я вместо этого реализую A как:

а-hack.h

template<typename T>
struct A
{
    int get() const;
};

template<typename T>
int A<T>::get() const
{
    return 0;
}

template<>
inline
int A<long>::get() const
{
    return 1;
}

extern template class A<int>;
extern template class A<long>;

a-hack.cc

#include "a-hack.h"

template class A<int>;
template class A<long>;

и скомпилировать снова, это работает как ожидалось

% g++ -Wall -g -D'A_H="a-hack.h"' a-hack.cc main.cc
% ./a.out 
ai=0 al=1

Тем не менее, в моем реальном примере это приводит к сбою программы с g++ 4.1.2 (при работе для g++ 4.4.4). Я не сузил точную причину аварии (ошибка сегментации). Он появляется только в том случае, если указатель стека поврежден в том, что было бы вызовом A < > :: get().

Я понимаю, что явное создание шаблона не является стандартным на данный момент, но ожидал ли кто-нибудь, что я сделал выше, чтобы работать? Если нет, то каков правильный способ сделать это?

Спасибо

4b9b3361

Ответ 1

extern template class A<long>;

В этой строке указано, что A<long> должен быть явно создан в соответствии с определениями, которые уже видел компилятор. Когда вы добавите специализацию позже, вы нарушите это значение.

Добавить объявление вашей специализации в файл заголовка.

template <typename T> struct A { /*...*/ };
template<> int A<long>::get() const;
extern template class A<int>;
extern template class A<long>;

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


Обратите внимание, что объявление extern template не требуется, если вы имеете дело с одним объектом шаблона (в отличие от этого случая, когда мы должны проинструктировать компилятор о классе A<long> и функции A<long>::get()). Если вы хотите специализировать шаблон функции в другой единицы перевода, достаточно написать просто template<>.

template<typename T> int freeGet() { return 0; }  // you can even add "inline" here safely!
template<> int freeGet<long>();  // this function is not inline (14.7.3/12)

Но вы должны иметь <>. Если вы опустите <>, объявление превратится в явное инстанцирование реализации по умолчанию (return 0), что, скорее всего, не то, что вы хотели! Даже если вы добавите extern, компилятору разрешено встроить эту реализацию по умолчанию; если ваш код неожиданно ломается при передаче -O2, вы, возможно, случайно опустили <>.

Ответ 2

Добавление этого ответа для ответа на вопрос в заголовке (создание экземпляра шаблона и не обязательно создание экземпляра шаблона).

Это очень похоже на объявление/определение функции.

  • Явное объявление экземпляра: extern template class - это объявление и обычно должно входить в заголовок.
  • Явное определение экземпляра: template class является определением и обычно должно идти в cpp.
  • В объектных файлах, следующих за объявлением, не генерируется код.
  • Код создается в объектном файле cpp, который содержит определение.
  • Без явного инстанцирования неявное создание экземпляра будет иметь место, когда шаблон фактически используется. Это произойдет в каждом компиляторе (объектном файле), который использует шаблон.
  • Неявное создание экземпляра не произойдет, если объявление было встречено. Это суть этого механизма - избегать дублирования объектного кода, вызванного неявной инстанцированием, которая произошла из-за того, что компилятор не верил, что существует единая единица компиляции, ответственная за создание экземпляра шаблона.

Подробнее здесь.