Шаблоны C ++ должны быть определены (при условии полного тела функции) в той же единице перевода (файл .CPP плюс все включенные файлы заголовков), где они используются. В вашем заголовочном файле все, что вы сделали, это объявили (учитывая имя и подпись) функции. В результате, когда вы включаете base.h
, все, что видит компилятор, это:
class Base {
template <typename T> void test(T a);
}
Это объявляет, но не определяет функцию. Чтобы определить его, вы должны включить тело функции:
class Base {
template <typename T> void test(T a)
{
// do something cool with a here
}
}
Причина, по которой это необходимо, заключается в том, что компилятор C ++ генерирует код для шаблонов по мере необходимости. Например, если вы звоните:
Base obj;
obj.test< int >( 1 );
obj.test< char >( 'c' );
Компилятор сгенерирует два набора машинного кода на основе шаблона Base::test
, один для int
и один для char
. Ограничение здесь заключается в том, что определение шаблона Base::test
должно быть в одной и той же единице перевода (файл .CPP), иначе компилятор не будет знать, как создать машинный код для каждой версии функции Base::test
. Компилятор работает только с одним модулем перевода за раз, поэтому он не знает, определили ли вы Base::test< T >
в каком-либо другом файле CPP. Он может работать только с тем, что имеет под рукой.
Это сильно отличается от того, как работают дженерики в C #, Java и аналогичных языках. Лично мне нравится думать о шаблонах как о текстовом макросе, который расширяется компилятором по мере необходимости. Это заставляет меня помнить, что полное тело функции шаблона должно быть включено в любой файл CPP, где она используется.