I try to learn a little bit about template metaprogramming and currently i play around with variadic templates.
In his talk "Variadic Templates are Funadic" Alexandrescu introduces a small tuple implementation, which i try to build and maybe extend a little bit. (I know it is a toy example, i just try to learn a little bit more about c++). However, i have a small problem with his code.
Here it is:
template <typename... Ts>
class tuple
{};
template<size_t, typename> struct tuple_element;
template<typename T, typename... Ts>
struct tuple_element<0, tuple<T, Ts...>>
{
typedef T type;
};
template <size_t k, typename T, typename... Ts>
struct tuple_element<k, tuple<T, Ts...>>
{
typedef
typename tuple_element<k-1,tuple<Ts...>>::type type;
};
template<size_t k, typename... Ts>
typename std::enable_if<k == 0,
typename tuple_element<0,tuple<Ts...>>::type&>::type
get(tuple<Ts...>& t)
{return t.head_;}
template<size_t k, typename T, typename... Ts>
typename std::enable_if<k != 0,
typename tuple_element<k,tuple<T,Ts...>>::type&>::type
get(tuple<T,Ts...>& t)
{
tuple<Ts...> & super = t;
return get<k-1>(super);
}
template <typename T, typename... Ts>
class tuple<T,Ts...> : private tuple<Ts...>
{
private:
T head_;
};
int main(int argc, char *argv[])
{
tuple<int,std::string> t;
get<0>(t) = 10;
get<1>(t) = std::string("test");
std::cout<<get<0>(t)<<std::endl;
}
In order to work correctly, the get function must be friend of the tuple class (It is also mentioned on this slides, see 32). But how does the friend declaration looks like? I tried different approaches but could not get it to work. When i change the code from private to public inheritance and change the access rules for head_ to public it works.
Thanks for your help
Kevin
This works for me:
template <typename T, typename... Ts>
class tuple<T,Ts...> : private tuple<Ts...>
{
private:
T head_;
template<size_t k, typename T1, typename... T1s>
friend typename std::enable_if<k != 0,
typename tuple_element<k,tuple<T1,T1s...>>::type&>::type
get(tuple<T1,T1s...>& t);
template<size_t k, typename... T1s>
friend typename std::enable_if<k == 0,
typename tuple_element<0,tuple<T1s...>>::type&>::type
get(tuple<T1s...>& t);
};
Demo.
Another implementation from the other point of view:
#include <iostream>
#include <type_traits>
template <class... Args>
class Tuple;
template <>
class Tuple<> {};
template <class T, class... Args>
class Tuple<T, Args...>: public Tuple<Args...> {
using Base = Tuple<Args...>;
T Value_;
public:
Tuple(T&& value, Args&&... args)
: Value_(std::forward<T>(value))
, Base(std::forward<Args>(args)...)
{
}
T& Value() {
return Value_;
}
};
template <size_t k, class T, class... Args>
struct Select {
using Type = typename Select<k - 1, Args...>::Type;
};
template <class T, class... Args>
struct Select<0, T, Args...> {
using Type = T;
};
template <size_t k, class... Args>
using TSelect = typename Select<k, Args...>::Type;
template <bool P, class T>
using TEnableIf = typename std::enable_if<P, T>::type;
template <size_t k, class T, class... Args>
TEnableIf<(k != 0), TSelect<k, T, Args...>&> get(Tuple<T, Args...>& t) {
return get<k - 1, Args...>(t);
}
template <size_t k, class T, class... Args>
TEnableIf<(k == 0), TSelect<k, T, Args...>&> get(Tuple<T, Args...>& t) {
return t.Value();
}
int main() {
Tuple<int, char> t(1, 'a');
std::cout << get<0>(t) << std::endl;
std::cout << get<1>(t) << std::endl;
get<1>(t) = 'b';
std::cout << get<1>(t) << std::endl;
}
Actually, we don't need a Tuple to get a type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With