template<typename... Types>
struct Foo;
template<typename T , typename... Types>
struct Foo<T, Types ...> : public Foo<Types ...>
{
Foo( T member , Types ... others ) : Foo<Types ...>( others... ), m_member( member )
{
}
T m_member;
};
template<typename T>
struct Foo<T>
{
Foo( T member ) : m_member( member )
{
}
T m_member;
};
int main()
{
Foo<char,int,bool,double> f( 'a' , 42 , true , 1.234 );
}
I found this code somewhere on SO and I am wondering if it's completely useless? It seems to me that all members are called m_member
so how would I access them?
If I do cout << f.m_member;
it will print 'a', but I see no way to access the other members.
In your current implementation each derived Foo
class shadows its parent's m_member
. It is up to you how to implement a logic of accessing each field (via indexing, types, other).
One possibility is to access them by an overloaded templated member function taking a type or an index (reversed for simplicity):
#include <type_traits>
#include <cstddef>
template <typename... Types>
struct Foo;
template <typename T, typename... Types>
struct Foo<T, Types...> : Foo<Types...>
{
// bring get() member functions from parent class into current scope
using Foo<Types...>::get;
Foo(T member, Types... others) : Foo<Types...>{others...}, m_member{member} {}
template <typename U>
auto get(T* = nullptr)
-> typename std::enable_if<std::is_same<U, T>::value, T&>::type
{
return m_member;
}
template <std::size_t N>
auto get(T* = nullptr)
-> typename std::enable_if<N == sizeof...(Types), T&>::type
{
return m_member;
}
private:
T m_member;
};
template <typename T>
struct Foo<T>
{
Foo(T member) : m_member{member} {}
template <typename U>
auto get(T* = nullptr)
-> typename std::enable_if<std::is_same<U, T>::value, T&>::type
{
return m_member;
}
template <std::size_t N>
auto get(T* = nullptr)
-> typename std::enable_if<N == 0, T&>::type
{
return m_member;
}
private:
T m_member;
};
Tests:
Foo<char, int, bool, double> a{ 'a', 42, true, 1.234 };
assert('a' == a.get<char>());
assert(42 == a.get<int>());
assert(true == a.get<1>());
assert(42 == a.get<2>());
a.get<char>() = 'b';
assert('b' == a.get<3>());
For other implementations that provide an access to members see std::tuple<...>
with its std::get<N>()
.
A canonical implementation looks as below:
#include <type_traits>
#include <cstddef>
template <typename... Types>
struct Foo;
template <typename T, typename... Types>
struct Foo<T, Types...> : Foo<Types...>
{
Foo(T member, Types... others) : Foo<Types...>{others...}, m_member{member} {}
T m_member;
};
template <typename T>
struct Foo<T>
{
Foo(T member) : m_member{member} {}
T m_member;
};
template <std::size_t N, typename T>
struct element;
template <typename T, typename... Types>
struct element<0, Foo<T, Types...>>
{
using type = T;
};
template <std::size_t N, typename T, typename... Types>
struct element<N, Foo<T, Types...>>
{
using type = typename element<N - 1, Foo<Types...>>::type;
};
template <std::size_t N, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<N == 0, T&>::type
{
return f.m_member;
}
template <std::size_t N, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<N != 0
, typename element<N, Foo<T, Types...>>::type&
>::type
{
Foo<Types...>& p = f;
return get<N - 1>(p);
}
template <typename U, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<std::is_same<T, U>::value, T&>::type
{
return f.m_member;
}
template <typename U, typename T, typename... Types>
auto get(Foo<T, Types...>& f)
-> typename std::enable_if<!std::is_same<T, U>::value, U&>::type
{
Foo<Types...>& p = f;
return get<U>(p);
}
Tests:
Foo<char, int, bool, double> a{ 'a', 42, true, 1.234 };
assert(true == get<2>(a));
assert(42 == get<int>(a));
get<char>(a) = 'b';
assert('b' == get<0>(a));