Ryan Ryan - 2 months ago 15
C++ Question

Call "base" template function from C++ template specialization that "overrides" it?

The question:

Is there a way to call the "base" template function from a specialized template function in C++, the way a child class can access the parent's versions of virtual methods when overriding them? (NOTE: I suspect the answer is "no" but would love to be wrong)

The context:

I often find myself specializing a template function only because a special case needs extra pre- or post-processing, not because the "guts" of the code have changed.

To give a contrived example:

With inheritance, you can do the following:

struct base {
virtual void go() { printf("%p", this); }
struct foo : base {
virtual void go() { printf("this foo lives at "); base::go(); }

... and calling foo::go() will print "this foo lives at <address>"

With templates, though:

template <typename T>
void go(T const &t) { printf("%p\n", &t); }

template <>
void go(foo const &f) {
printf("this foo lives at ");
??? how to access "base" template ???

You can work around the problem in an ugly way by factoring out a mess of little helper functions and specializing them instead of the function you actually care about:

template <typename T>
void _go_pre(T const &t) { /* do nothing */ }

template <typename T>
void _go_post(T const &t) { /* do nothing */ }

template <typename T>
void go(T const &t) {
_go_pre(t); /* just in case */
printf("%p\n", &t);

void _go_pre(foo const &t) { printf("this foo lives at "); }

... but that clutters the code significantly because now the "base" template needs to anticipate all the ways a "child" specialization might override it, and most types will use few, if any, of those hooks. The clutter becomes unreadable and unmaintainable pretty quickly, because the reason for these hooks is not known at the point where they are defined, and you have to test the different combinations of used/unused hooks.

All this is exactly like the problems you'd have with virtual method overrides in a world where a child class couldn't access original version provided by the parent class.


It's not possible directly. However, you can do with fewer (and IMO less ugly) helpers like this:

template <typename T>
void base_go(T const &t) { printf("%p\n", &t); }

template <typename T>
void go(T const &t) { base_go(t); }

void go(foo const &t) { printf("this foo lives at "); base_go(t); }

As an alternative, you could put the base_ variants into a separate namespace instead of giving them modified names.