Search code examples
c++iteratorc++20std-ranges

Is there a std::view (or something like that) which occupies every nth element?


This is only a toy example, but assume I have a matrix like class

union {
    std::array<T, M * N> m_x;
    std::array<vector<T, M>, N> m_col;
};

and I want to access the ith row of the matrix in the sense that I can pass begin and end of this row to another function. Is there any lightweight (i.e. without introducing computational overhead) construction available for that?

Imagine for example that we want to compute a matrix-times-matrix multiplication. I would like to compute the element in the ith row and jth column (which is an inner product) by passing the ith row of the first and jth column of the second matrix to std::transform(std::execution::par_unseq, ...).

I could use boost::counting_iterator, for example, but I would like to stick with the standard library.


Solution

  • Yes, there is std::views::stride in C++23, but no such counterpart exists in C++20.

    std::array<int, 9> matrix{0, 1, 2, 3, 4, 5, 6, 7, 8};
    
    for (int x : matrix | std::views::stride(3)) {
        std::cout << x << ' ';
    }
    

    See live example at Compiler Explorer

    This prints

    0 3 6
    

    However, this feature is quite new, and you need a recent compiler to use it at this moment. It's also questionable how much could be gained in terms of readability by using std::views for matrix multiplication, but who am I to judge? ;)

    In C++20 you could use the range-v3 library to accomplish the same thing:

    #include <range/v3/view/stride.hpp>
    #include <array>
    #include <iostream>
    
    int main() {
        std::array<int, 9> matrix{0, 1, 2, 3, 4, 5, 6, 7, 8};
    
        for (int x : matrix | ranges::views::stride(3)) {
            std::cout << x << ' ';
        }
    }
    

    See live example at Compiler Explorer

    Further notes

    The union declaration that you have written might be a mistake. Remember that you cannot access there non-active member of a union outside of specific special cases.

    See also Is it allowed to use unions for type punning, and if not, why?

    Since this question is and you may not have access to std::views::stride(3), you could imitate this yourself.

    template <typename T, std::size_t N, std::size_t M>
    struct matrix {
        std::array<T, N * M> data;
        
        std::span<T, 3> row(std::size_t i) {
            return {data.begin() + i * N, data.begin() + (i + 1) * N};
        }
    
        // TODO: similarly, return a view into a column with a column() function
        //       this function should return something like a span, except with a stride
    };
    

    You could then iterate over the rows and columns of a matrix by iterating over m.row(i) and m.column(j).