Cool C++0X features IV: Variadic templates again

Post by Nico Brailovsky @ 2011-05-03 | Permalink | 2 comments | Leave a comment

Last time we finally solved the varargs problem. Let's review what we learned: * Variadic templates let us create something receiving a variable set of arguments * We can process the head of that set, then recursively process the tail * It adds weird new syntax + When declaring typename... T you are saying "here goes a list of types" + When declaring T... t you are saying t is a list of objects with different type + When you write t..., you are saying "expand the list of arguments" * It's type safe * It's very neat to confuse your coworkers

So, what can we do with it besides implementing our own version of printf? Let's do something better, let's try adding up a list of numbers to start flexing our variadic templatefooness (?).

What's the usual way of adding a list of numbers? In templates, that is. We need something like this:

sum (H:T) <- H + sum(T)
sum () <- 0

Of course, in C++ templates you don't have values, you just have types. We could implement it like this (if this looks like a new language you may want to check my template metaprogramming series):

#include 
struct Nil{};
template  struct Lst {
    typedef H Head;
    typedef T Tail;
};
template <
        template class Op,
        typename Head,
        typename Lst>
struct intForeach
{
    typedef typename intForeach
        < Op, typename Lst::Head, typename Lst::Tail >::result Next;
    typedef typename Op< Head, Next >::result result;
};
template <
        template class Op,
        typename Head>
struct intForeach 
{
    typedef Head result;
};
template <
        typename Lst,
        template
        class Op>
struct Reduce
{
    typedef typename intForeach
        < Op, typename Lst::Head, typename Lst::Tail >::result result;
};
template  struct Num {
    const static int value = N;
};
template  struct Sum {
    static const int r = A::value + B::value;
    typedef Num result;
};
int main() {
    std::cout << Reduce<
        Lst<Num<2>, Lst<Num<4>, Lst<Num<6>, Lst< Num<8> > > > >,
        Sum >::result::value << "n";
    return 0;
}

Nothing too fancy, plain old recursion with a sum. Yet it's quite verbose, can we make this a little bit more terse and, hopefully, more clear? Yes, we can. Take a look at that Lst, Lst<...> It sucks. And it's the perfect place to use variadic templates, we just need to construct a structure getting a list of ints, like this:

template <
    // The operation we wish to apply
    template class Op,
    // Current element to process
    class H,
    // All the rest
    class... T>
struct Reduce_V
{
    // TODO
}

That one should look familiar from last time article. Now, to implement a reduce operation we need to operate the current element with the result of reducing the tail, so we have to do something like this:

// Remember how T... means to expand T for the next instance
    typedef typename Reduce_V::result Tail_Result

There's something missing. Can you see what? The ending condition, of course. Let's add it and we'll get something like this:

template <
        // The operation we wish to apply
        template class Op,
        // All the rest
        class... T>
struct Reduce_V
{
};
template <
        // The operation we wish to apply
        template class Op,
        // All the rest
        class H>
struct Reduce_V
{
    typedef H result;
};
template <
        // The operation we wish to apply
        template class Op,
        // Current element to process
        class H,
        // All the rest
        class... T>
struct Reduce_V
{
        // Remember how T… means to expand T for the next instance
   typedef typename Reduce_V::result Tail_Result;
   // Reduce current value with the next in line
   typedef typename Op::result result;
};

And using it is very simple too:

std::cout << Reduce_V< Sum, Num<1>, Num<2>, Num<3>, Num<4>>::result::value << "n";

Next time we'll see another example for variadic templates and a new C++0x feature.


In reply to this post, Chaitanya commented @ 2013-09-06T22:10:04.000+02:00:

Thanks for all the C++ posts. They are quite informative, really appreciate the C++ blog series :)

Original published here.


In reply to this post, nico commented @ 2013-09-06T22:42:15.000+02:00:

Glad you find them useful Chaitanya, thanks for the feedback

Original published here.