38

Obviously the code below doesn't compile in C++. But I have a case where I'd like to parameterize a class with zero or more data items based on template parameters.

Is there any way I can declare a class whose data members depend on variadic template parameters so I can access each of them? or some other way to achieve what I'd like?

This came up in a real program which I've solved an entirely different way but now I'm interested in the more abstract problem of how I might have done this.

template <typename... Types> class Data
{
    // Declare a variable of each type in the parameter pack
    // This is NOT valid C++ and won't compile...
    Types... items;
};

struct Item1
{
    int a;
};

struct Item2
{
    float x, y, z;
};

struct Item3
{
    std::string name;
}

int main()
{
    Data<Item1, Item2> data1;
    Data<Item3> data2;
}
jcoder
  • 29,554
  • 19
  • 87
  • 130

4 Answers4

25

You could use a std::tuple

#include <tuple>

template <typename... Types> class Data
{
    std::tuple<Types...> items;
};

struct Item1
{
    int a;
};

struct Item2
{
    float x, y, z;
};

struct Item3
{
    std::string name;
};

int main()
{
    Data<Item1, Item2> data1;
    Data<Item3> data2;
}

Try it here

Jonas
  • 6,915
  • 8
  • 35
  • 53
11

That is the purpose of std::tuple:

template <typename... Types> class Data
{
    std::tuple<Types...> items;
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    Ah thank you. That's slightly embarrassing, I use tuple but somehow didn't think to use it with a parameter pack expansion here. – jcoder Nov 24 '16 at 08:53
9

The standard has got you covered. Just declare std::tuple<Types...> items.

MSalters
  • 173,980
  • 10
  • 155
  • 350
8

I wanted to be able to use the members with fold expressions. So I ended up with:

template <class T, class... rest> class hold : hold<rest...> {
    using base = hold<rest...>;
    T v_;

public:
    hold(T v, rest... a) : base(a...), v_(v) {}

    template <class F, class... args> auto apply(F f, args... a) {
        return base::apply(f, a..., v_);
    }
};

template <class T> class hold<T> {
    T v_;

public:
    hold(T v) : v_(v) {}

    template <class F, class... args> auto apply(F f, args... a) {
        return f(a..., v_);
    }
};

This then facilitates:

template <class scalar, class... arrays> struct plus_expr {
    hold<arrays...> a_;

    plus_expr(arrays... a) : a_(a...) {}

    scalar operator[](index const i) {
        return a_.apply([i](arrays... a) { return (a[i] + ...); });
    }
};
Bowie Owens
  • 2,798
  • 23
  • 20
  • 1
    Huge thanks! I was able to use this general structure in C++11 to make a tuple-like class that can do std::apply style behavior. I had similar need for indexing like your example too. I took a different approach though. Instead of the lambda capture, I passed it in to apply, and then instead of apply(f, a..., v_) it was apply(f, a..., _sample(v_, i)) which was overloaded for different "array-like" inputs. – kylefinn May 13 '19 at 17:17