Как правильно специализировать шаблон при использовании «внешнего шаблона»? - PullRequest
21 голосов
/ 18 марта 2011

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

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

ah

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
 %

Если я закомментирую две строки «внешнего шаблона» в ах, все скомпилируетсяи работать как ожидается с обоими компиляторами.Я предполагаю, что в зависимости от существования явного создания экземпляра при отсутствии «внешнего шаблона» поведение не определено даже в C ++ 0x, в противном случае, какой смысл в C ++ 0x добавление «внешнего шаблона»?

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

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 ().

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

Спасибо

Ответы [ 2 ]

16 голосов
/ 18 марта 2011
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 не требуется, если вы имеете дело с одним шаблоном (в отличие от этого случая, когда мы должны указать компилятору оба класса * 1016). * и функция 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, компилятору будет разрешено inline этой реализации по умолчанию; если ваш код неожиданно ломается при передаче -O2, возможно, вы случайно пропустили где-то <>.

3 голосов
/ 24 декабря 2015

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...