Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

An Introduction to Variadic Templates in C++0x : Page 3

Templates are one of the most powerful features in C++, and variadic templates make them even more powerful. Variadic templates in the C++0x standard library can greatly simplify the writing of type-safe code with variable numbers of arguments.


advertisement

Uses of Variadic Class Templates

Function templates can really benefit from the use of variadic template parameters because of the tie-in with variadic function parameters, but class templates can benefit too. Just as with function templates, the benefit comes in when you need a list of types but class templates support default template parameters. You've been able to fake it in the past by specifying a reasonably-sized maximum number of parameters and defaulting all the parameters to a dummy type. The key change with variadic templates is therefore the lifting of the arbitrary limit. It also makes the templates easier to write. For example, boost::variant allows you to declare a variable that can be an instance of one of several types. For instance, boost::variant<int,std::string> can either hold an int or a std::string.

In the current release of boost, you can specify up to 20 types in the list through the use of the boost preprocessor and metaprogramming libraries. With variadic templates, this could be unbounded, allowing you to declare a variable that can hold an instance of one of 100 types. It also simplifies the code as you no longer need to rely on preprocessor and metaprogramming tricks.

A similar case is std::tuple, except it holds an instance for each entry in the list rather than the either/or choice of boost::variant. You can write a simple tuple class quite easily. First, declare that simple_tuple is a variadic template without specifying any of the details.



template<typename ... Types> class simple_tuple;

Then you specialize it for an empty list: if there aren't any types in the list, then you haven't got any values. So it's just an empty class.

template<> class simple_tuple<> {};

Now you can specialize for a list of at least one element by recursion. A list of N elements is a single element plus a list of N-1 elements.

template<typename First,typename ... Rest> class simple_tuple<First,Rest...>: private simple_tuple<Rest...> { First member; public: simple_tuple(First const& f,Rest const& ... rest): simple_tuple<Rest...>(rest...), member(f) {} First const& head() const { return member; } simple_tuple<Rest...> const& rest() const { return *this; } };

The simple accessor functions allow you to get at the data:

  • To access the first element of a tuple, you can call t.head().
  • To access the second, you call t.rest().head().
  • To access the third, you call t.rest().rest().head()
  • And so forth

This works, but it's a bit unwieldy, which is why std::tuple has a helpful get() function to retrieve a numbered element. You can write a get_tuple_entry() function for your tuple too (see Listing 1).

In order to obtain the type and value of the N-th element of your simple tuple, you need to use a helper class (simple_tuple_entry) because you cannot partially specialize a function template. The get_tuple_entry function itself just passes everything on to simple_tuple_entry, either to retrieve the type of the entry or to retrieve the value itself.

The simple_tuple_entry class again has two specializations. The first is for the 0-th element, which is therefore first in the list and corresponds to the head() function for the tuple. If your index is not zero, you still need a list of at least one element. In this case, you discard the first element and find the (index-1)-th element of the rest() of your original tuple.

This get_tuple_entry function makes element accesses much easier: you can just say get_tuple_entry<5>(t) to get the sixth element of your tuple. The following simple code will thus output "42,a,3.141":

int main() { simple_tuple<int,char,double> st(42,'a',3.141); std::cout<<get_tuple_entry<0>(st)<<"," <<get_tuple_entry<1>(st)<<"," <<get_tuple_entry<2>(st)<<std::endl; }

Of course std::tuple has a lot more features and is correspondingly more complex, but the basis is still something similar to this approach.

Power to the Templates

Templates are one of the most powerful features in C++, and variadic templates make them even more powerful. They are used in many places in the C++0x standard library to enable passing of arbitrary numbers of function arguments in places such as std::thread, std::bind and std::function, and to allow building tuples of arbitrary size with std::tuple. Variadic templates can greatly simplify the writing of type-safe code with variable numbers of arguments.



Anthony Williams is the Technical Director for Just Software Solutions Ltd., where he spends most of his time developing custom software for clients, mostly for Windows, and mostly C++. He is the maintainer of the Boost Thread library and is also a member of the BSI C++ Standards Panel. His latest book, "C++ Concurrency in Action: Practical Multithreading" is currently available in the Early Access Edition from Manning's web site.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap