В обоих случаях вы делаете явное создание экземпляра. Во втором случае создается только ABC<char>::foo
, в то время как в первом случае также создается ABC<char>::bar
.
Другой аналогичный пример может прояснить последствия:
// test.h
template <typename T>
class ABC {
public:
void foo( T& );
void bar( T& );
};
// test.cpp
template <typename T>
void ABC<T>::foo( T& ) {} // definition
template <typename T>
void ABC<T>::bar( T& ) {} // definition
template void ABC<char>::foo( char & ); // 1
template class ABC<char>; // 2
// main.cpp
#include "test.h"
int main() {
ABC<char> a;
a.foo(); // valid with 1 or 2
a.bar(); // link error if only 1, valid with 2
}
В этом примере в main
компилятор не может видеть определения foo
или bar
, поэтому он не может создавать экземпляры методов. При обработке main.cpp компилятор с радостью примет код в main, поскольку вы говорите ему, что ABC
является шаблоном и что он имеет эти две функции, и будут предполагать, что они будут определены в некотором другом модуле перевода.
В модуле перевода, который содержит test.cpp, компилятор видит оба определения метода, и оба экземпляра (метод / класс) могут быть полностью обработаны. Если присутствует только экземпляр метода ([1]), компилятор сгенерирует только этот метод и оставит bar
неопределенным. Таким образом, любой код, который включает test.h, ссылается на скомпилированный test.cpp и использует только метод foo
, будет компилировать и ссылаться, но использование bar
не сможет связать его, поскольку он не определен.
Явное создание экземпляра шаблона класса создает символы для всех методов-членов, и в этом случае любая единица перевода, которая включает test.h и ссылки на скомпилированный объектный файл test.cpp, будет компилироваться и связываться.