У меня есть класс с функцией шаблона члена:
// writer.h
class Writer {
public:
...
template <typename T, typename V>
void addField(const std::string& name, V v)
{
// write something
}
};
И в исходном файле Writer, я добавил явные специализации для some_type
:
// writer.cpp
template <>
void Writer::addField<some_type, int>(const std::string& name, int v)
{
// specific some_type writing logic
}
Это работает … иногда. Даже если я определенно удостоверился, что у меня есть правильные типы:
writer.addField<some_type>("name", static_cast<int>(some_value));
Иногда вызывается явная специализация, а иногда вызывается основная. Что дает?
Объявление специализаций в исходном файле может привести к возникновению всевозможных тонких проблем, которые очень трудно диагностировать. Компилятор также не обязан вам здесь помогать. Стандарт настоятельно рекомендует вам не делать этого, с помощью лимерика, в [temp.expl.spec] / 6-7:
Если шаблон, шаблон элемента или элемент шаблона класса явно специализированы, то эта специализация
должны быть объявлены до первого использования этой специализации, которая вызовет неявную реализацию
иметь место, в каждой единице перевода, в которой происходит такое использование; Диагностика не требуется. Если программа
не дает определения явной специализации, и специализация используется таким образом,
это приведет к неявной реализации или член является виртуальной функцией-членом,
Программа некорректна, диагностика не требуется. Неявная реализация никогда не генерируется для явного
специализация, которая объявлена, но не определена.Размещение явных объявлений специализации для шаблонов функций, шаблонов классов, шаблонов переменных,
функции-члены шаблонов классов, […] и т. д. могут влиять на правильность формирования программы в соответствии с
к относительному позиционированию деклараций явной специализации и их точкам реализации
в блоке перевода, как указано выше и ниже. При написании специализации, будьте осторожны с ее
место нахождения; или сделать его компиляцией будет таким испытанием, чтобы разжечь его самосожжение.
Вполне вероятно, что в некоторых единицах перевода специализация была объявлена до первого использования, а в некоторых единицах перевода — нет. Лучше всего избегать таких проблем, просто объявив свою специализацию в заголовке:
// writer.h
class Writer {
public:
...
template <typename T, typename V>
void addField(const std::string& name, V v)
{ /* ... */ }
};
// still writer.h
template <>
inline void Writer::addField<some_type, int>(const std::string& name, int v)
{ /* ... */ }
Вы также можете просто объявлять это в заголовке (больше не должно быть встроенным), и все еще определять это в источнике.