2

I have a class with several members of "similar" type like:

class Container {

    C1 c1;
    C2 c2;
    C3 c3;
    ....

    template <typename T>
    const T& get() {
        ????
    }
};

The class has a templated method get<T>() which can be used to get a reference to the member of type T - i.e.

Container cont;

const auto& c1 = cont.get<C1>();

to access the c1 member. The current implementation of get<T>() is based on specialization for all the types represented in the Container class. Is there an elegant way to achieve the same without manually implementing the specializations?

user422005
  • 1,989
  • 16
  • 34

2 Answers2

3

You can use a constexpr tuple of pointers to data members to solve your problem with std::get. This implements the behavior without change to the existing implementation and offers a safe and easy modification point.

#include <tuple>

struct C1 {};
struct C2 {};

class Container 
{
public:
    template <typename T>
    const T& get() 
    {
        constexpr auto ptr_tuple = std::make_tuple(
            &Container::c1,
            &Container::c2);

        // T Container::* is a pointer to a data member of `Container` which has the type `T`
        auto member_ptr = std::get<T Container::*>(ptr_tuple);
        return this->*member_ptr;
    }

private:
    C1 c1;
    C2 c2;
    
};

int main()
{
    Container c;
    const C1 & foo = c.get<C1>();
    const C2 & bar = c.get<C2>();
}

If you need to select members based on template arguments often, you can move the tuple out of get() and make it a constexpr static data member instead :

class Container 
{
public:
    template <typename T>
    const T& get() 
    {
        auto member_ptr = std::get<T Container::*>(ptr_tuple);
        return this->*member_ptr;
    }

private:
    C1 c1;
    C2 c2;
    
    static constexpr auto ptr_tuple = std::make_tuple(
        &Container::c1,
        &Container::c2);   
};
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
2

Is there an elegant way to achieve the same without manually implementing the specializations?

Starting from C++17, if constexpr

template <typename T>
T const & get() const
 {
   if constexpr ( std::is_same_v<T, C1> )
      return c1;
   else if constexpr ( std::is_same_v<T, C2> )
      return c2;
   else if constexpr ( std::is_same_v<T, C3> )
      return c3;
 }

Before C++17... the best I can imagine is the creation of a std::tuple<C1, C2, C3> and the return of the std::get<T>(tuple)

max66
  • 65,235
  • 10
  • 71
  • 111