Ответ 1
Вы можете только специализировать его явно, предоставляя все аргументы шаблона. Не допускается частичная специализация для функций-членов шаблонов классов.
template <typename T,bool B>
struct X
{
void Specialized();
};
// works
template <>
void X<int,true>::Specialized()
{
...
}
Обходной задачей является введение перегруженных функций, которые имеют преимущество по-прежнему в одном классе, и поэтому они имеют одинаковый доступ к переменным-членам, функциям и материалам
// "maps" a bool value to a struct type
template<bool B> struct i2t { };
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl(i2t<B>()); }
private:
void SpecializedImpl(i2t<true>) {
// ...
}
void SpecializedImpl(i2t<false>) {
// ...
}
};
Обратите внимание, что, перейдя к перегруженным функциям и нажав параметры шаблона в параметр функции, вы можете произвольно "специализировать" свои функции, а также при необходимости их планировать. Другой распространенный метод - отнестись к шаблону класса, определенному отдельно
template<typename T, bool B>
struct SpecializedImpl;
template<typename T>
struct SpecializedImpl<T, true> {
static void call() {
// ...
}
};
template<typename T>
struct SpecializedImpl<T, false> {
static void call() {
// ...
}
};
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl<T, B>::call(); }
};
Я нахожу, что обычно требуется больше кода, и я считаю, что функция перегрузки легче обрабатывать, в то время как другие предпочитают отложить путь к шаблону класса. В конце концов это вопрос вкуса. В этом случае вы могли бы разместить этот другой шаблон внутри X
тоже как вложенный шаблон - в других случаях, когда вы явно специализируетесь, а не только частично, то вы не можете этого сделать, потому что вы можете размещать явные специализации только в пространстве имен а не в класс.
Вы также можете создать такой шаблон SpecializedImpl
только для целей перегрузки функции (тогда он работает аналогично предыдущему i2t
), как показано в следующем варианте, который также оставляет первую переменную параметра (так что вы можете позвонить он с другими типами - не только с текущими параметрами шаблона экземпляра)
template <typename T,bool B>
struct X
{
private:
// maps a type and non-type parameter to a struct type
template<typename T, bool B>
struct SpecializedImpl { };
public:
void Specialized() { Specialized(SpecializedImpl<T, B>()); }
private:
template<typename U>
void Specialized(SpecializedImpl<U, true>) {
// ...
}
template<typename U>
void Specialized(SpecializedImpl<U, false>) {
// ...
}
};
Я думаю, что иногда лучше отложить переход к другому шаблону (когда дело доходит до таких случаев, как массивы и указатели, перегрузка может быть сложной и просто пересылка в шаблон шаблона мне была проще), а иногда просто перегрузка внутри шаблона лучше - особенно если вы действительно пересылаете аргументы функции, и если вы касаетесь переменных-членов классов.