Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Variadic Templates, Part I

Last updated Jan 1, 2003.

TR1's tuple class template allows you to pack an arbitrary number of objects with arbitrary types in a single container-like object. The underlying implementation of class templates that take a variable number of arguments in contemporary C++ is problematic and limiting, though. The addition of variadic templates to C++0x will make the implementation of such classes much simpler, ridding library implementers of many of the limitations and performance bottlenecks that they are facing today. Let's examine variadic templates in detail.

The Problem

The canonical example that demonstrates the need for variadic templates in C++ is class tuple:

tuple<int, float> t2;
tuple<char, string, short, double> t4;

Tuples set an arbitrary limit on the number of arguments they can store. According to the C++0x standard, the minimum number of elements allowed is 10. The reason for specifying such a low minimum is that implementers are unable to directly express in contemporary C++ the notion that a function or class template takes an arbitrary number of arguments. Instead, they use various emulations, including extra defaulted template parameters and preprocessor metaprogramming (Yes, macros are back with a vengeance!). Using the preprocessor to emulate "variadic" templates eliminates a large amount of code duplication that would occur otherwise. However, it comes with many problems. Take for example the std::tr1::function class. The original GCC implementation of that class supported 20 parameters but the excessive compilation time forced the implementers to revert to a maximum of 10 parameters instead. Even with 10 parameters, the code of std::tr1::function still requires too much time to preprocess and parse. In addition, it's nearly unintelligible. Let's look at a concrete example.

To implement a tuple that accepts up to 10 parameters of unrelated types, library vendors use default template parameters, with a dummy struct serving as default value for every parameter:

struct unused;
template<typename T1 = unused, typename T2 = unused, typename T3 = unused, 
template<//_ up to _N parameters
        typename TN = unused> class tuple;

This tuple can be used with anywhere from zero to n template arguments. However, most of the specializations of that class template will have a large number of unused arguments:

tuple <char, short, int, long long> eightosixtyfourbits;

Here, six template parameters are unused, but they still lead to long compilation times and cryptic compilation errors. One way to optimize the compiler-generated code of such classes is to provide a list of partial specializations as follows:

class tuple<> { /* zero-argument version*/ };
template<typename T1>

class tuple<T1> { /* single argument version*/ };
template<typename T1, typename T2>
class tuple<T1, T2> { /* two-argument version*/ };

This trick ensures that the code produced for a given number of arguments is optimized and correct. However, it also leads to a huge amount of code repetition and extremely long type names in error messages (many compilers print the defaulted arguments anyway). There is a more serious flaw with this approach -- the fixed upper limit on the number of arguments that can be provided.

To solve these problems, a new C++0x feature called variadic templates was voted into the Working Paper some time ago. Let's see which advantages variadic templates offer.

Variadic Templates

C++0x variadic templates provide an intuitive and efficient mechanism for expressing function and class templates that take an arbitrary number of arguments (in this part I will focus only on variadic class templates). The authors of the variadic templates proposal specifically designed this feature to eliminate the need for most uses of preprocessor meta-programming. In addition, variadic templates are type-safe and improve argument forwarding.

Using this new feature, you can explicitly state that tuple accepts one or more template parameters like this:

template<typename... Elements> class tuple;

The ellipsis (which you probably recognize from printf()-style variable arguments function declarations) to the left of Elements indicates that Elements is a template type parameter pack. A parameter pack is a new notion. Unlike an ordinary parameter, which can only bind to a single argument, a parameter pack is a single identifier grouping zero or more template arguments. Thus, in the following typedef declaration:

typedef tuple <char, short, int, long long> eightosixtyfourbits;

The parameter pack Elements binds to a list of arguments: char, short, int and long long whereas in this declaration, the parameter pack Elements binds to three arguments:

typedef tuple <float, double, long double> floating_point;

In the next part of this series, I will show how to unpack a parameter pack and present variadic function templates.