lezebulon
lezebulon

Reputation: 7994

Retrieve given member of variadic class by type

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

Answers (3)

Jarod42
Jarod42

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

lezebulon
lezebulon

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

T.C.
T.C.

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

Related Questions