product(1)

2つのiterableの直積です。タプルを返します。

template<typename T, typename U>
class cProduct : public cIterable<tuple<T,U>> {
    shared_ptr<cIterable<T>>    gen1;
    shared_ptr<cIterable<U>>    gen2;
    vector<U>   v;
    typename vector<U>::const_iterator  p;
    int     mode;
    
public:
    cProduct(shared_ptr<cIterable<T>> g1, shared_ptr<cIterable<U>> g2) :
                                            gen1(g1), gen2(g2), mode(0) { }
    
    bool exists_next() {
        if(mode == 0) {
            if(!gen1->exists_next())
                return false;
            else if(!gen2->exists_next())
                return false;
            
            mode = 1;
            v.push_back(gen2->value());
            return true;
        }
        else if(mode == 1) {
            if(gen2->exists_next()) {
                v.push_back(gen2->value());
                return true;
            }
            else if(gen1->exists_next()) {
                p = v.begin();
                mode = 2;
                return true;
            }
            else {
                return false;
            }
        }
        else {
            ++p;
            if(p != v.end()) {
                return true;
            }
            else if(gen1->exists_next()) {
                p = v.begin();
                return true;
            }
            else {
                return false;
            }
        }
    }
    tuple<T,U> value() const {
        T   y = mode == 1 ? gen2->value() : *p;
        return tuple<T,U>(gen1->value(), y);
    }
};

template<typename T, typename U>
shared_ptr<cIterable<tuple<T,U>>> product(shared_ptr<cIterable<T>> g1,
                                            shared_ptr<cIterable<U>> g2) {
    return shared_ptr<cIterable<tuple<T,U>>>(new cProduct<T,U>(g1, g2));
}

これも第2引数はvectorに値を保持しています。

int main() {
    cout << product(range(2), range(3)) << endl;
}
(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2)