Как явные шаблонные инстанцирования влияют на то, что может найти компоновщик?

См. следующий код и очистите мои сомнения.

  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 
} 
10
задан 9 revs, 5 users 42% 29 April 2010 в 12:57
поделиться

3 ответа

В обоих случаях вы выполняете явное создание экземпляра. Во втором случае создается только ABC :: foo , а в первом случае также создается ABC :: 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 неопределенным.Таким образом, любой код, который включает test.h, ссылается на скомпилированный test.cpp и использует только метод foo , будет компилироваться и компоноваться, но использование bar не сможет связать из-за того, что он неопределенный.

Явное создание экземпляра шаблона класса генерирует символы для всех методов-членов, и в этом случае любая единица трансляции, которая включает test.h и ссылки на скомпилированный объектный файл test.cpp, будет компилироваться и связываться.

13
ответ дан 3 December 2019 в 23:48
поделиться

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

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

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

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

0
ответ дан 3 December 2019 в 23:48
поделиться

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

0
ответ дан 3 December 2019 в 23:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: