Я хотел бы добавить к полезному ответу, предоставленному @Vaughn.
В определении шаблона test
у вас есть неопределяемое объявление функции-члена, а также определение функции-члена определения.Но это определение и объявление функции связано с окружающим шаблоном test<T>
, а не с классом test<float>
, который в итоге будет создан из него!
Если вы неявно создаете экземпляр test<float>
из этого шаблона, например, с помощью ::
-оператора (области действия) или создавая определение переменной этого типа и т. Д., То декларации , но необязательно определения всех членов также неявно созданы.Все они связаны с test<float>
.
и членами * Так что, когда вы пишете явную специализацию для test<float>::testFunc
, потому что вы использовали ::
, чтобы заглянуть в класс test<float>
, этот класссоздается неявно и вместе с ним все объявления его членов.
Определение из test<float>::testFunc
создается только тогда, когда это требуется (например, когда вы его вызываете).
Явная специализация, которую вы объявили тогда, повторно объявляет функцию-член, которая ранее была неявно реализована, и дает определение, что это , связанный с test<float>
.Это реальная функция, и ее нельзя безопасно определить в заголовке, если она не помечена inline
- в противном случае вы рискуете «определить более одного раза», если включите в его заголовок несколько таймеров.
Суммируя, какие декларации и определения существуют для testFunc
и откуда они берутся:
По определению вашего шаблона:
test<T>::testFunc(int);
test<T>::testFunc(int) { }
Генерируется специализация компилятором из test<T>::testFunc(int);
test<float>::testFunc(int);
Явная специализация вами test<T>::testFunc(int);
test<float>::testFunc(int);
test<float>::testFunc(int) { }
В частности, не существует сгенерированного определения test<float>::testFunc
.Если бы это было так, то ваш код был бы некорректно сформирован, без необходимости диагностики, потому что сгенерированное определение могло бы помешать вашей явной специализации, которая предоставила определение для той же функции.Но такая вещь может произойти только в том случае, если вы вызываете ее создание до явной специализации.Поэтому, если вы перенесете явное определение специализации в файл .cpp
(если вы не хотите, чтобы оно было inline
), поместите явное определение специализации , как показано в @Vaughn, в заголовок.