Как явные экземпляры шаблона влияют на то, что может найти компоновщик? - PullRequest
10 голосов
/ 29 апреля 2010

См. Следующий код и, пожалуйста, проясните мои сомнения.

  1. Поскольку ABC является шаблоном, почему он не показывает ошибку, когда мы помещаем определение функции-члена класса ABC в test.cpp?

  2. Если я добавлю код test.cpp в test.h и получу remve 2, он будет работать нормально. Почему?

.

// 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 
} 

Ответы [ 3 ]

13 голосов
/ 29 апреля 2010

В обоих случаях вы делаете явное создание экземпляра. Во втором случае создается только 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, будет компилироваться и связываться.

0 голосов
/ 29 апреля 2010

Я думаю, вы хотели иметь {} вместо; в № 1.

0 голосов
/ 29 апреля 2010

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

#1 создает экземпляр шаблона класса и как часть этого создает все его методы.

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

Разницу можно увидеть, если вы введете зависящую от типа ошибку в bar () (например, оператор типа void *x = b;). Вы получите ошибку компилятора с #1, но не с #2. Также обратите внимание, что компилятор (по крайней мере, gcc) не скомпилирует #1 с последующим #2, но скомпилирует один из них без другого, или если за #2 следует #1.

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