0

Short version: I need to pass a template class a parameter pack, which is the result of applying a function to another parameter pack. This needs to work within a using statement.

Background: As a challenge, I'm writing a generic C++11 version of python's zip(). In order to do so, I have written a generic zipIterator template class which can be used to iterate over many iterators simultaneously, yielding a tuples of their values. For example:

#include "zip.hpp"
#include <iostream>
#include <vector>
#include <tuple>

int main(){
    std::vector<int> vec = {0,1,2,3};
    char arr[] = {'a','b', 'c'};
    zipIterator<decltype(vec.begin()), char*, decltype(vec.rbegin())>
        itr(vec.begin(), std::begin(arr), vec.rbegin());
    zipIterator<decltype(vec.begin()), char*, decltype(vec.rbegin())>
        end(vec.end(), std::end(arr), vec.rend());
    for(; itr!=end; ++itr){
        std::cout << "(" << std::get<0>(*itr) << ", " << std::get<1>(*itr)
            << ", " << std::get<2>(*itr) << ")" << std::endl;
    }
}
//output:
//(0, a, 3)
//(1, b, 2)
//(2, c, 1)

The Problem I would like to make a zip container class which can be passed containers, and which zips over them by calling std::begin() and std::end() on each one. So far I have this:

template<typename... Containers>
class zip{
public:
    using iterator = zipIterator<???>;
    zip(Containers... cs) : begin_(iterator(std::begin(cs)...)),
        end_(iterator(std::end(cs)...)){};
    iterator begin() {return begin_;}
    iterator end() {return end_;}
private:
    iterator begin_;
    iterator end_;
};

My question is: what goes in the place of ??? to make this work? So far I have tried std::begin(std::declval<Containers>())..., decltype(std::begin(Containers)...), std::result_of<std::begin(Containers)>::type..., and many more variations on this.

Sorry if this is a repeat. I read the following Stack Overflow answers and they all seem to be related, but I don't think they are quite what I am looking for:

Community
  • 1
  • 1
  • 1
    Your constructor takes the `Containers` by value, which (a) is probably less efficient than you would like due to the copying, and (b) will result in undefined behavior when you use later use the invalid stored iterator values since the constructor parameters will be destroyed after it completes. You probably want references to the `Containers` instead. – Casey Apr 12 '15 at 05:16
  • Good catch--I meant to have it take them by reference. Thanks! – VikingofRock Apr 13 '15 at 01:35

1 Answers1

1
using iterator = zipIterator<decltype(std::begin(std::declval<Containers&>()))...>;

The basic idea is that ... expands the pattern on its left. Here, the pattern is decltype(std::begin(std::declval<Containers&>())) - the type of the return value of std::begin when called on an lvalue of type Containers.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Thank you for this, and for the explanation. That helped a lot. One more question--it seems like the '&' is required in "std::declval()"--why is that? – VikingofRock Apr 13 '15 at 01:39
  • @VikingofRock `std::begin` doesn't work that great with rvalue containers (they bind to the `const &` overload) and not at all with rvalue arrays. After all, it doesn't make much sense to retrieve an iterator into a temporary that's about to be destroyed. `declval()` is an rvalue; `declval()` is an lvalue. – T.C. Apr 13 '15 at 02:00