Определения шаблонов вне класса

Это O.K. определить виртуальную функцию шаблона класса вне ее тела? Виртуальная функция не может быть встроена, но чтобы избежать множественных определений в единицах компиляции, они должны быть отмечены inline (при условии, что заголовки шаблонов будут включены в несколько исходных файлов). С другой стороны, компилятор может игнорировать inline, поэтому это кажется правильным. В качестве примера приведен правильный код:

template <typename T>
class C
{
public:
    virtual void f(T val);
};

template <typename T>
inline
void C<T>::f(T val)
{
  //definition
}

?

BTW gcc (3.4.2) позволяет пропустить inline до определения функции f(T val), но не до аналогичной функции обычного класса. Это только gcc-поведение?

Ответ 1

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

// everything in the header:
template <class T>
class A
{
  static int i;
};

template <class T>
int A<T>::i=0;

Стандартная цитата: (3.2/5)

Может быть несколько определений типа класса (раздел 9), тип перечисления (7.2), встроенная функция с внешняя связь (7.1.2), шаблон класса (раздел 14), шаблон нестатической функции (14.5.6), элемент статических данных шаблона класса (14.5.1.3), функции-члена шаблона класса (14.5.1.1) или специализации шаблона для которые некоторые параметры шаблона не указаны (14.7, 14.5.5) в программе, при условии, что каждое определение появляется в отдельной единицы перевода и при условии, что определения удовлетворяют следующим требованиям...

Требования в основном говорят, что два определения должны быть идентичными.

Это не работает в случае обычных классов. Должно быть не более одного определения во всей программе.

Ответ 2

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

Это потому, что функция шаблона не генерирует само определение, если оно не полностью специализировано. Чтобы доказать свою точку зрения, выполните следующие действия:

void C<int>::f(int)
{
}

приведет к ошибке компоновщика, так как функция имеет определение в этом случае. (при условии, что вы включили это в несколько единиц перевода. Если вы пометили его inline:

inline void C<int>::f(int)
{
}

ошибка больше не возникает.

Ответ 3

Вы можете определить функции там, если любой код, который должен создать экземпляр рассматриваемой функции, имеет видимость этого кода во время компиляции (не время ссылки).

Весьма распространено разделение шаблона на 2 файла, один из которых является традиционным заголовком, а второй - реализацией, как и в случае не-шаблонных функций и их реализации. Единственное различие заключается в том, что вам нужно # включить файл реализации шаблона, а также заголовок, когда вы хотите его использовать.