Fun with Variadic Templates: Part I

If you’re a C++ user, you’ve probably heard that the up and coming language revision, C++0x, is adding support for variadic templates. And if you’re anything like me, perhaps your first question was “Cool, so what good are they?” And I would say “Excellent question!” to such an inquiry, because C++ has been doing variadic style templates and functions for a while now. Even C could do variadic functions with its ellipsis operator and vararg API. Combine that with function overloading and default function arguments and you’ve got yourself a pretty powerful variadic function system.

The same goes for C++’s templates. Have you ever wondered how Boost’s Tuple library works? Well, Boost does a lot of cool complex things, like defining variadic templates using preprocessor and template metaprogramming, but in the end it’s actually pretty simple:

struct NullArg {};
template<typename Type1 = NullArg, typename Type2 = NullArg, typename Type3 = NullArg> 
class tuple {
        tuple(T0 t0) {}
        tuple(T0 t0, T1 t1) {}
        tuple(T0 t0, T1 t1, T2 t2) {}
};
 
tuple<int, char> t(1, '2');

Of course this tuple class doesn’t store or let you retrieve arguments, only allows for up to three parameters (adding more arguments, while monotonous without some preprocessor fun, is easy), and there’s some definite metaprogramming magics that go into tuple’s get<X>() template function, but it’s all still possible.

Getting back to the point: alright so we can, in effect, do variadic templates already, even without fancy C++0x. So why are they good? Well, all we’re really able to do is emulate variadic templates. The compiler always sees those “variadic” structs as having the maximum number of parameters, which means extremely long mangled names (increased binary size), slower compilation time, stupidly complex compiler diagnostic output, and did I mention slower compilation time? Not to mention that it’s an ugly and hacky way to achieve something that could be much more elegant. It’s fine for libraries to define handy structs with variadic emulation, but do you really want your production code nastied up like that? Likely not.

So, in come’s C++0x’s variadic templates. They’re easy, fun, and the whole family can use them! The syntax looks like this:

template<typename... Args> 
class tuple {};
 
tuple<char, int, bool> t;

Not so useful yet, but as you can see the ellipsis operator is given new purpose, and in this case it’s meant to denote a template parameter pack. It can also be used to denote a parameter pack expansion, like this:

template<typename... Args> 
class tuple {
        tuple(Args...) {};
};
 
tuple<char, int, bool> t('1', 2, false);

Here we’ve created a variadic tuple template and expanded the Args parameter pack in the constructor, thus letting us pass a variable number of arguments to the tuple’s constructor.

You can use the pack expansion pretty much anywhere it makes sense: base classes, constructor initializers, you name it, but in my opinion the real power of variadics comes once you start using them in conjunction with template functions and we begin to utilize C++’s powers of argument deduction. Take a look at this:

template<typename... Args>
struct tuple {
        tuple(Args...) {};
};
 
template<typename... Args>
tuple<Args...> make_tuple(Args... args) {
        return tuple<Args...>(args...);
}
 
int main() {
        auto t1 = make_tuple(1, 2, 3, 4, 5);
        return 0;
}

Nice and clean! There’s no need to specify all the template arguments to make_tuple because the compiler deduces them from the function arguments! Also note the use of the C++0x auto keyword there. Auto variables are still statically typed, they just get their type set to whatever initializes them.

So there’s the ultra-basics. Up next I’m going to start exploring what you can really do with variadic templates — like how you can extract an arbitrary element from a pack, pack searching, and other fun compile time trickery. You’d think Bjarne might have wanted to allow random access to variadic parameter lists, but that just wouldn’t be any fun. Why do things with random access when you can do it all with recursion!

Ready for more? Head to Part II!

Share this article:
  • E-mail this story to a friend!
  • Reddit
  • StumbleUpon
  • Digg
  • Technorati
  • Slashdot
  • del.icio.us
  • Facebook
  • LinkedIn
  • TwitThis
  • Google