Re: partial specialization of a single class member, without specializing the rest of the class?
On Dec 1, 1:25 am, "Jonathan Thornburg [remove -animal to reply]"
<jth...@astro.indiana-zebra.edu> wrote:
Sorry to followup on my own posting, but I've found an "other means",
i.e. a workaround. It's not pretty, but it does seem to work.
Just to recall the context, I have 4 policy classes A, B, X, and Y,
and a template class P with two template parameters, AB and XY. All
4 combinations P<A,X>, P<B,X>, P<A,Y>, and P<B,Y> are valid. P has
a member function P::foo() which I can implement with generic code.
However, it also has a member function P::bar() whose implementation
requires separate code paths for XY=X and XY=Y. (In my real application
these code paths must be separate at compile time, as the XY=Y code
uses Y members which don't exist in X.) The problem is how to implement
partial specialization of P::bar() for XY=X and XY=Y without also having
to duplicate P::foo(). In my previous posting I gave what seems to me
to be the obvious code; g++ says that code is illegal.
Here's my workaround:
....
// forward declarations
template <class AB, class XY> class P;
template <class AB> void bar_X_doit(P<AB,X>& self);
template <class AB> void bar_Y_doit(P<AB,Y>& self);
template <class AB, class XY> inline void bar_forwarder(P<AB,XY>&
self);
// partial specialization of non-class functions isn't allowed,
// so we define a fully specialized bar_forwarder()
// for each of the 4 possible combinations of the AB and XY template
args
template <>
inline void bar_forwarder<A, X>(P<A,X>& self) { bar_X_doit<A>(self);
}
template <>
inline void bar_forwarder<B, X>(P<B,X>& self) { bar_X_doit<B>(self);
}
template <>
inline void bar_forwarder<A, Y>(P<A,Y>& self) { bar_Y_doit<A>(self);
}
template <>
inline void bar_forwarder<B, Y>(P<B,Y>& self) { bar_Y_doit<B>(self);
}
....
// the actual partially-specialized code for P<AB,X>::bar()
template <class AB>
void bar_X_doit(P<AB,X>& self)
{
// code which assumes class X, but still handles AB = either A or B
// ... 'self' is needed here to access things like self.foo()
}
// the actual partially-specialized code for P<AB,Y>::bar()
template <class AB>
void bar_Y_doit(P<AB,Y>& self)
{
// code which assumes class Y, but still handles AB = either A or B
// ... 'self' is needed here to access things like self.foo()
}
If you use a helper class instead of helper functions you can use
partial specialisation, avoiding the need for the extra forwarding
function.
template<class AB, class XY> struct bar_impl;
template<class AB>
struct bar_impl<AB, X> {
static void apply(P<AB, X>& self)
{
// code which assumes class X, but still handles AB = either A or
B
// ... 'self' is needed here to access things like self.foo()
}
};
template<class AB>
struct bar_impl<AB, Y> {
static void apply(P<AB, Y>& self)
{
// code which assumes class Y, but still handles AB = either A or
B
// ... 'self' is needed here to access things like self.foo()
}
};
template<class AB, class XY>
struct P
{
void bar() { bar_impl<AB, XY>::apply(*this); }
friend struct bar_impl<AB,XY>;
};
Which is not much more than the original specialisation would be (if
it were legal).
// explicit template instantiations
template class P<A, X>;
template class P<B, X>;
template class P<A, Y>;
template class P<B, Y>;
I presume these were to test the solution, because they're not needed
for it to work.
While this solution works, IMHO it's seriously inelegant. Can anyone
suggest a cleaner way to partially specialize a member function of a
class template, without having to duplicate all the rest of the class?
template<class AB, class XY>
struct P_impl
{
// Put the full implementation of P here, except for bar.
// Any members needed by bar should be protected.
}
template<class AB, class XY>
struct P;
template<class AB>
struct P<AB, X> : P_impl<AB, X> {
void bar()
{
// code which assumes class X, but still handles AB = either A or
B
}
};
template<class AB>
struct P<AB, Y> : P_impl<AB, Y> {
void bar()
{
// code which assumes class Y, but still handles AB = either A or
B
}
};
You'll have difficulty doing this if the rest of P needs to call bar.
(It can be done with downcasting.)
Yechezkel Mett
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]