Ответ 1
В обоих случаях вы выполняете явное инстанцирование. Во втором случае создается только 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 будет с радостью принимать код в основном, так как вы говорите ему, что ABC
является шаблоном и что он имеет эти две функции и будет предполагать, что они будут определены в некоторой другой единицы перевода.
В блоке трансляции, который содержит test.cpp, компилятор видит оба определения метода, и оба экземпляра (метод/класс) могут быть полностью обработаны. Если присутствует только экземпляр метода ([1]), компилятор только сгенерирует этот метод и оставит bar
undefined. Таким образом, любой код, который включает в себя test.h, ссылки на скомпилированный test.cpp и использует только метод foo
, будет компилировать и связывать, но использование bar
не будет связываться из-за того, что оно undefined.
Явное копирование шаблона класса генерирует символы для всех методов-членов, и в этом случае любая единица перевода, включающая test.h и ссылки на скомпилированный файл объекта test.cpp, будет компилироваться и связываться.