Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic templates and Alexandrescu tuple implementation

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

like image 878
IcePic Avatar asked Sep 13 '14 14:09

IcePic


2 Answers

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.

like image 170
T.C. Avatar answered Nov 19 '22 00:11

T.C.


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.

like image 34
Dmitriy Dokshin Avatar answered Nov 19 '22 00:11

Dmitriy Dokshin