I’ve never yet used the C++0x variadic templates feature but the following code compiles on G++ 4.5:
template <typename... Args>
struct tuple;
template <typename T, typename... Args>
struct tuple<T, Args...> {
T value;
tuple<Args...> inner;
};
template <typename T>
struct tuple<T> {
T value;
};
However, initializing them is … weird because we need to nest the inner values:
int main() {
tuple<int> n1 = { 23 };
tuple<int, float> n2 = { 42, { 0.5f } };
tuple<std::string, double, int> n3 = { "hello, world", { 3.14, { 97 } } };
}
Retrieving the values is of course a bit tedious. The simplest method is probably to provide a get<N>()
function template.
But we cannot implement get
directly since function templates cannot be partially specialized. Either we need to use SFINAE (read: boost::enable_if
) or we need to delegate the actual function of get
to a type that can be partially specialized.
In the following, I did the latter. But first, we need another helper type trait: nth_type
, which returns the appropriate return type of the get
function:
template <unsigned N, typename... Args>
struct nth_type;
template <unsigned N, typename T, typename... Args>
struct nth_type<N, T, Args...> : nth_type<N - 1, Args...> { };
template <typename T, typename... Args>
struct nth_type<0, T, Args...> {
typedef T type;
};
Easy-peasy. Just returns the nth type in a list of types.
Now we can write our get
function:
template <unsigned N, typename... Args>
inline typename nth_type<N, Args...>::type get(tuple<Args...>& tup) {
return get_t<N, Args...>::value(tup);
}
Like I said, this just delegates the task. No biggie. In practice, we probably want to have another overload for const
tuples (but then, in practice we would use an existing tuple
type).
Now for the killing, followed by a light salad:
template <unsigned N, typename... Args>
struct get_t;
template <unsigned N, typename T, typename... Args>
struct get_t<N, T, Args...> {
static typename nth_type<N, T, Args...>::type value(tuple<T, Args...>& tup) {
return get_t<N - 1, Args...>::value(tup.inner);
}
};
template <typename T, typename... Args>
struct get_t<0, T, Args...> {
static T value(tuple<T, Args...>& tup) {
return tup.value;
}
};
And that’s it. We can test this by printing some values in our previously defined variables:
std::cout << get<0>(n1) << std::endl; // 23
std::cout << get<0>(n2) << std::endl; // 42
std::cout << get<0>(n3) << std::endl; // hello, world
std::cout << get<1>(n2) << std::endl; // 0.5
std::cout << get<1>(n3) << std::endl; // 3.14
std::cout << get<2>(n3) << std::endl; // 97
Man, it’s fun messing with variadic templates.