The purpose of this paper is to propose mechanisms and rationale for named parameter packs and literal parameter packs. In particular, this proposal makes parameter packs suitable for use as typelists [1] and allows them to be defined and referenced from outside of template classes.
<int, std::basic_ostream<char>, 7>
typedef<signed char, short int, int, long int, long long int> signed_integral_types;
cout << contains<signed_integral_types, int>::value; // Prints true
// Inherits from true_type if typelist TL includes the type T
template<<typename...> TL, typename T> struct includes;
// Inherits from true_type if typelist TL includes all the types in TL2
template<<typename...> TL, <typename...> TL2> struct contains;
// The following asserts will not fire
static_assert(includes<<double, int>, int>::value, "unexpected compile error");
static_assert(contains<<double, float, int>, <float, int>>, "unexpected compile error");
tuple<signed_integral_types...> tsit;
// B has the same base classes as A
struct B : public direct_bases<A>::types... {};
template<typename T>
struct same_bases_as_T : public typename<typename...> direct_bases<T>::types... {};
Note that part of how we reduce ambiguity is by supporting a more limited problem domain than
considered in [5].The first question to ask of course is whether typelists belong in library or evolution. Many people have taken a library-based approach to typelists
template<typename... T> struct typelist {};
// Passing tuple's parameters to another template
template<template<typename...> class F, typename T> struct unwrap_into;
template<template<typename...> class F, typename... Ts>
struct unwrap_into<F, tuple<Ts...>> {
typedef F<Ts...> type;
};
typedef tuple<double, string> tds;
unwrap_into<map, tds>::type map_double_to_string;
By contrast, this is almost trivial when using parameter packs.
typedef<double, string> tds;
map<tds...> map_double_to_string;
What makes it worse is that how to expand the tuple's parameters can vary case by case. For example, to inherit from all of the types in a tuple, you might do something like
template<typename T> struct inherit;
template<typename... Ts>
struct inherit<tuple<Ts...>> : public Ts... {};
typedef tuple<interface1, interface2> interfaces;
struct myType : public inherit<interfaces> {};
With parameter pack-based typelists, it's no contest
typedef<interface1, interface2> interfaces;
struct myType : public interfaces... {};
struct A {
void f();
int g();
};
// Package the methods of A
typedef<&A::f, &A::g> A_methods;
For routines that manipulate parameter packs structurally regardless of type, it
would be nice to declare parameter packs with arbitrary parameters using a naked ....
we use “.” (by analogy with “...”) to represent
a single arbitrary template parameter.
For example, suppose we want to calculate the length of an (unexpanded) parameter pack without
resorting to the built-in sizeof... operator:
template<typename T> struct length;
template<>
struct length<<>> {
static constexpr int value = 0;
};
template<. t>
struct length<<t, ... Rest>> {
static constexpr int value = 1 + length<Rest>
};
template<typename T>
struct length<<T, ... Rest>> {
static constexpr int value = 1 + length<Rest>
};
typedef<int, 5, double, 6> pp;
static_assert(length<pp>::value == sizeof...(pp), "unexpected compile error");
template<... Ts> struct reverse;
template<>
struct reverse<> {
typedef<> type;
};
template<. T, ... Rest>
struct reverse<T, Rest...>
struct {
typedef <reverse<Rest...>::type..., T> type;
};
static_assert(is_same<reverse<char, int, vector<string>>::type, <vector<string>, int, char>>, "Unexpected compile error");
This also gives us a nice way of getting the ith item from a parameter pack, which Doug Gregor describes
in [] as “probably the most-requested feature for variadic templates” Perhaps something along the lines of
template<int i, typename T> struct at;
template<int i, . T, ... Rest>
struct at<i, <T, Rest...>> : public at<i - 1, Rest> {};
template<. t, ... Rest> // Notation from N3405
struct at<0, <t, Rest...>> {
static constexpr T value = t;
};
template<typename T, ... Rest>
struct at<0, <T, Rest...>> {
typedef T type;
};
typedef <int, 7> int7;
at<0, int7>::type i7 = at<1, int7>::value;
[1] Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied. Addison Wesley, 2001.
[2] Gregor, Järvi, Variadic Templates for C++0x; Journal of Object Technology (2008) 31-51. http://www.jot.fm/issues/issue_2008_02/article2/
[3] Spertus, Type traits and base classes, N2965=09-0155. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2965.html.
[4] The Boost MPL Library. http://www.boost.org/doc/libs/1_51_0/libs/mpl/doc/index.html.
[5] Variadic Templates in C++0x need some additional features to come closer to fulfilling their promise. https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c++/_-6X_xZlKlA.
[6] Loki Library. http://loki-lib.sourceforge.net/.
[7] “unpacking” a tuple to call a matching function pointer. http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/.
[8] Gregor, Powell, Järvi, Typesafe Variable-Length Function and Template Argument Lists