Search code examples
c++tuplesc++17variadic-templatesfold-expression

Apply function to tuple of vectors to get tuple of elements (variadic templates)


I have a container class which takes a list of vector or array objects (and stores a tuple of references to the original list of objects). Now, when calling the container via operator()(size_t idx) I want the function to return a tuple that contains the elements at index idx of each of the vectors/arrays.

    template<class... Ts>
    class Container
    {
    public:
        Container(const Ts&... objs)
            : m_data(objs...) { }

        auto operator()(size_t idx) const
        {
            const auto get_idx = [idx](const auto& obj) { return obj.at(idx);  };
            return std::tuple<const Ts::value_type& ...>((get_idx(m_data), ...));  // <-- does not compile
        }

    private:
        std::tuple<const Ts&...> m_data;
    };

    int main()
    {
        std::vector<int> v1{ 3,4,12,5 };
        std::vector<std::string> v2{ "on", "test", "ABC", "house" };
        std::array<double, 4> v3 = { 3.14, 2.71, 99.3, 128 };

        const auto z = Container(v1, v2, v3);

        auto v = z(2);  // <-- does not compile
        return 0;
    }

For example, z(2) above should be equal to std::tuple<int, std::string, double>(12, "ABC", 99.3). How would I achieve that?


Solution

  • You have a tuple and you need to turn it into a pack. The way to do that is with std::apply():

    auto operator()(size_t idx) const
    {
        return std::apply([=](auto const&... xs){
            return std::make_tuple(xs.at(idx)...);
        }, m_data);
    }
    

    xs here is a pack that refers to the contents of m_data. Once we have the pack, doing anything with it is straightforward.