Ответ 1
Частичная специализация шаблона функции, будь то шаблон элемента-члена или отдельный шаблон функции, не допускается стандартом:
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) говорится:
Список параметров шаблона элемента частичной специализации шаблона должен соответствовать списку параметров шаблона частичной специализации шаблона шаблона. [...]
Подробнее об этом читайте здесь:
С++ - перегружает шаблонный метод класса с частичной спецификацией этого метода
Итак, каково решение? Вы бы частично специализировали свой класс вместе со всей повторяющейся работой?
Простое решение - это делегирование работы, а не частная специализация шаблона класса. Напишите отдельный шаблон функции и определите его как:
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
не является шаблоном функции-члена. Его не охватывающий класс, который является шаблоном. Следовательно, мы можем частично специализировать шаблон шаблона в любое время, чтобы получить желаемый эффект специализированной функции шаблона функции членства. - Поскольку мы передаем указатель
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>
.
Это изменение требует от нас написания кода в несколько ином порядке. См. Полную демоверсию со всем порядком определений классов и функций и всего: