Скажем, я объявляю шаблон класса A
в a.h
#include <iostream>
template<bool b>
class A {
public:
void print(std::ostream& out);
};
И определить метод печати в a.cpp
(с явным указанием для true
а также false
)
#include "a.h"
template<bool b>
void A<b>::print(std::ostream& out) {
out << "A" << b;
}
template class A<true>;
template class A<false>;
Пример основной основной программы в main.cpp
может быть
#include "a.h"
int main() {
A<true> a;
a.print(std::cout);
}
Небольшой проект выше компилируется просто отлично.
Вопрос: Если я поставлю явные экземпляры выше определения print
метод (в a.cpp
), код больше не компилируется, с обычным undefined reference to A<true>::print(...)
ошибка.
#include "a.h"
template class A<true>;
template class A<false>;
template<bool b>
void A<b>::print(std::ostream& out) {
out << "A" << b;
}
Почему это так?
редактировать: Makefile для компиляции
main : main.o a.o
g++ main.o a.o -o main
main.o : main.cpp
g++ -c main.cpp
a.o : a.cpp
g++ -c a.cpp
Я не думаю, что есть хороший натуральный объяснение, почему это так. Ясно, что компилятор мог видеть определение функции-члена, даже если она предоставлена после явной реализации — потому что она находится в том же файле.
Тем не менее, компиляторы не требуются для этого; на самом деле это прямо запрещено Стандартом:
(§14.7.2 / 9) Явное определение экземпляра, которое именует специализацию шаблона класса, явно создает экземпляр специализации шаблона класса и является явным определением экземпляра только тех членов, которые были определены в момент создания.
Я думаю, что причины этого включают следующее:
Там может быть несколько явных специализаций для немного функции-члена позже в блоке перевода; также имеет смысл, в интересах программиста, иметь четкое правило о том, какие из них будут созданы;
Когда шаблон косвенным образом в конкретном случае принимаются во внимание только специализации, определенные до момента создания; поэтому правило одинаково для неявных и явных реализаций.
template class A<true>;
template class A<false>;
та же самая причина почему обычно ожидается, что код шаблона определяется в самом заголовке. Чтобы выполнить явную реализацию, вы (компилятор) должны иметь возможность увидеть полное определение класса шаблона, что невозможно из вашего main.cpp
,
тем не мение a.cpp
имеет доступ ко всем определениям класса (здесь метод print), поэтому здесь работает явная реализация.