Частичная специализация шаблона функции, будь то шаблон функции-члена или автономный шаблон функции, не допускается Стандартом:
template<typename T, typename U> void f() {} //okay - primary template
template<typename T> void f<T,int>() {} //error - partial specialization
template<> void f<unsigned char,int>() {} //okay - full specialization
Но вы можете частично специализировать сам шаблон класса. Вы можете сделать что-то вроде этого:
template <class A>
class Thing<A,int> //partial specialization of the class template
{
//..
int doSomething();
};
template <class A>
int Thing<A,int>::doSomething() { /* do whatever you want to do here */ }
Обратите внимание, что когда вы частично специализируете шаблон класса, тогда список параметров шаблона функции-члена (по определению вне класса) должен соответствовать списку параметров шаблона частичной специализации шаблона класса. Это означает, что для вышеуказанной частичной специализации шаблона класса вы не можете определить это:
template <class A>
int Thing<A,double>::doSomething(); //error
Это недопустимо, поскольку список параметров шаблона в определении функции не соответствует списку параметров шаблона частичной специализации шаблона класса. §14.5.4.3 / 1 из Стандарта (2003) гласит:
Список параметров шаблона члена частичной специализации шаблона класса должен соответствовать Список параметров шаблона частичной специализации шаблона класса. [...]
Подробнее об этом читайте в моем ответе здесь:
C ++ - метод класса шаблонов с перегрузкой с частичной спецификацией этого метода
Так в чем же решение? Вы бы частично специализировали свой класс вместе со всей повторяющейся работой?
Простым решением будет делегирование работы вместо частичной специализации шаблона класса. Напишите автономный шаблон функции и специализируйте его следующим образом:
template <class B>
B doTheActualSomething(B & b) { return b; }
template <>
int doTheActualSomething<int>(int & b) { return b + 1; }
И затем вызвать этот шаблон функции из doSomething()
функции-члена как:
template <class A, class B>
B Thing<A,B>::doSomething() { return doTheActualSomething<B>(b_); }
Поскольку в вашем конкретном случае doTheActualSomething
необходимо знать значение только одного члена, а именно b_
, вышеприведенное решение подойдет, так как вы можете передать значение функции в качестве аргумента чей тип является шаблоном тип аргумент B
, а специализация для int
возможна при полной специализации.
Но представьте, что если требуется доступ к нескольким элементам, тип каждого зависит от шаблона тип аргумент-список, то определение шаблона автономной функции не решит проблема, потому что теперь в шаблоне функции будет более одного типа аргумента, и вы не сможете частично специализировать функцию для, скажем, одного типа ( как это не допускается).
Таким образом, в этом случае вы можете вместо этого определить шаблон класса, который определяет статическую не шаблонную функцию-член doTheActualSomething
. Вот как:
template<typename A, typename B>
struct Worker
{
B doTheActualSomething(Thing<A,B> *thing)
{
return thing->b_;
}
};
//partial specialization of the class template itself, for B = int
template<typename A>
struct Worker<A,int>
{
int doTheActualSomething(Thing<A,int> *thing)
{
return thing->b_ + 1;
}
};
Обратите внимание, что вы можете использовать указатель thing
для доступа к любому члену класса. Конечно, если ему нужен доступ к закрытым членам, вы должны сделать struct Worker
другом Thing
шаблона класса, например:
//forward class template declaration
template<typename T, typename U> struct Worker
template <class A, class B>
class Thing
{
template<typename T, typename U> friend struct Worker; //make it friend
//...
};
Теперь передайте работу другу как:
template <class A, class B>
B Thing<A,B>::doSomething()
{
return Worker<A,B>::doTheActualSomething(this); //delegate work
}
Здесь следует отметить два момента:
- В этом решении
doTheActualSomething
не является функцией-членом template . Это не включающий класс, который является шаблоном. Следовательно, мы можем частично специализировать шаблон класса в любое время, чтобы получить желаемый эффект частичного специализации шаблона функции-члена.
- Поскольку мы передаем указатель
this
в качестве аргумента функции, мы можем получить доступ к любому члену класса Thing<A,B>
, даже к частным, поскольку Worker<T,U>
также является другом.
Полная онлайн-демонстрация: http://www.ideone.com/uEQ4S
Теперь все еще есть шанс на улучшение. Теперь все экземпляры шаблона класса Worker
являются друзьями всех экземпляров шаблона класса Thing
. Таким образом, мы можем ограничить эту дружбу многие ко многим следующим образом:
template <class A, class B>
class Thing
{
friend struct Worker<A,B>; //make it friend
//...
};
Теперь только один экземпляр шаблона класса Worker
является другом одного экземпляра шаблона класса Thing
. Это дружба один на один. То есть Worker<A,B>
- друг Thing<A,B>
. Worker<A,B>
НЕ друг Thing<A,C>
.
Это изменение требует от нас написания кода в несколько ином порядке. Посмотрите полную демонстрацию со всеми порядками определений классов и функций и всеми:
http://www.ideone.com/6a1Ih