Какой правильный способ специализировать шаблон при использовании "шаблона 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().
Я понимаю, что явное создание шаблона не является стандартным на данный момент, но ожидал ли кто-нибудь, что я сделал выше, чтобы работать? Если нет, то каков правильный способ сделать это?
Спасибо
Ответы
Ответ 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, который содержит определение.
- Без явного инстанцирования неявное создание экземпляра будет иметь место, когда шаблон фактически используется. Это произойдет в каждом компиляторе (объектном файле), который использует шаблон.
- Неявное создание экземпляра не произойдет, если объявление было встречено. Это суть этого механизма - избегать дублирования объектного кода, вызванного неявной инстанцированием, которая произошла из-за того, что компилятор не верил, что существует единая единица компиляции, ответственная за создание экземпляра шаблона.
Подробнее здесь.