Reputation: 7994
here is a recursive class definition :
template<class... T>
class Mgr2
{
};
template<class T, class... args>
class Mgr2<T, args...>
{
Container<T> _container;
Mgr2<args...> _tail;
public:
Mgr2() { };
};
I'd like to implement the following :
Mgr2<int, double> mgr;
mgr.get<int>(); // retrieves the Container<int> element
How could I do this? I tried to do several things but failed.... a free function is fine too, and I don't care if the behaviour is undefined if there are 2 "int" in the class defintion for instance
thanks !
Upvotes: 2
Views: 136
Reputation: 217408
With std::tuple
, and std::get
from C++14:
template<typename... Ts>
class Mgr2
{
private:
std::tuple<Container<Ts>...> _containers;
public:
Mgr2() {};
template <typename T>
const Container<T>& get() const { return std::get<Container<T>>(_containers); }
template <typename T>
Container<T>& get() { return std::get<Container<T>>(_containers); }
};
Demo.
If you are stick to C++11, You may write your own get<T>
in C++11.
Upvotes: 1
Reputation: 7994
I simply found out that it can be easily done like this:
template<class... T>
class CptMgr
{
};
template<class T, class... args>
class CptMgr<T, args...>
{
CptContainer<T> _container;
CptMgr<args...> _tail;
public:
template<class U>
CptContainer<U>& get() { return _tail.get<U>(); };
template<>
CptContainer<T>& get() { return _container; };
}
Upvotes: 0
Reputation: 137315
Here's a sketch of one way to do this, by restructuring Mgr2
:
template<class T>
struct Leaf {
Container<T> container;
};
template<class... Ts>
struct Mgr2 : Leaf<Ts>... {
template<class T>
Container<T> &get() {
return static_cast<Leaf<T>&>(*this).container;
}
};
Having duplicate types result in a compile-time error as soon as Mgr2
is instantiated.
If we want to permit duplicates, or indexing by an integer in addition to indexing by type, we can add an index parameter to Leaf
:
template<std::size_t I, class T>
struct Leaf {
Container<T> container;
};
and adjust Mgr2
:
template<class Seq, class...> struct Mgr2_Impl;
template<std::size_t... Is, class... Ts>
struct Mgr2_Impl<std::index_sequence<Is...>, Ts...>
: Leaf<Is, Ts>... { };
template<class... Ts>
struct Mgr2 : Mgr2_Impl<std::index_sequence_for<Ts...>, Ts...> {
private:
template<class T, std::size_t I>
Leaf<I, T>& do_get(Leaf<I, T>& leaf) { return leaf; }
template<std::size_t I, class T>
Leaf<I, T>& do_get(Leaf<I, T>& leaf) { return leaf; }
public:
template<class T>
decltype(auto) get() { return do_get<T>(*this).container; }
template<std::size_t I>
decltype(auto) get() { return do_get<I>(*this).container; }
};
If you want to keep your original design, you can use SFINAE or tag dispatch. Showing the former:
template<class U>
std::enable_if_t<std::is_same<U, T>{}, Container<U>&> get(){
return _container;
}
template<class U>
std::enable_if_t<!std::is_same<U, T>{}, Container<U>&> get(){
return _tail.template get<U>();
}
Upvotes: 1