c++pointersn-dimensional

Iterate throught n-dimensional vector c++


I wanted to write my own code to iterate over an n dimensional vector (where the dimension is known). Here is the code:

void printing(const auto& i, const int dimension){
    int k= dimension;
    for(const auto& j: i){
        if(k>1){
            cout<<"k: "<<k<<endl;
            printing(j, --k);
        }
        else{
            //start printing
            cout<<setw(3);
            cout<<j; //not quite sure about this line
        }
        cout<<'\n';
    }
}

I get an error:

main.cpp:21:5: error: ‘begin’ was not declared in this scope
 for(const auto& j: i){
 ^~~

Could someone help me to correct it or give me a better way to print the vector? Thanks in advance for your time.


Solution

  • If the dimensions are known at compile-time, this can be solved easily with a template that takes dimensions as the non-type argument.

    template <std::size_t Dimensions>
    void printing(const auto& i){
        if constexpr (Dimensions != 0) {
            for(const auto& j: i){
                // I'm not sure if it is intentional to print 'k' each iteration, 
                // but this is kept for consistency with the question
                cout<<"k: " << Dimensions << endl;
                printing<Dimensions - 1u>(j);
            }
        } else {
            cout << setw(3);
            cout << j;
            cout << '\n';
        }
    }
    

    The use would be, for a 2d vector:

    printing<2>(vec);
    

    Live Example


    However, if you always know that const auto& i will be a std::vector type, you can potentially solve this even easier by just not using auto arguments at all, and instead use template matching:

    // called only for the vector values
    template <typename T>
    void printing(const std::vector<T>& i){
        for(const auto& j: i){
            // possibly compute 'k' to print -- see below
            printing(j);
        }
    }
    
    // Only called for non-vector values
    template <typename T>
    void printing(const T& v) {
        cout << setw(3);
        cout << v;
        cout << '\n';
    }
    

    Live Example

    To compute the "dimension" of the vector, you can write a recursive type-trait for that:

    #include <type_traits> // std::integral_constant
    
    // Base case: return the count
    template <std::size_t Count, typename T>
    struct vector_dimension_impl
      : std::integral_constant<std::size_t, Count> {};
     
    // Recursive case: add 1 to the count, and check inner type
    template <std::size_t Count, typename T, typename Allocator>
    struct vector_dimension_impl<Count, std::vector<T,Allocator>> 
      : vector_dimension_impl<Count + 1u, T> {};
    
    // Dispatcher
    template <typename T>
    struct vector_dimension : vector_dimension_impl<0u, T> {};
    
    // Convenience access
    template <typename T>
    inline constexpr auto vector_dimension_v = vector_dimension<T>::value;
     
    // Simple tests:
    static_assert(vector_dimension_v<std::vector<int>> == 1u);
    static_assert(vector_dimension_v<std::vector<std::vector<int>>> == 2u);
    static_assert(vector_dimension_v<std::vector<std::vector<std::vector<int>>>> == 3u);
    

    Live Example

    With the above recursive trait, you can get the "dimension" of each templated vector type, without requiring the user to pass in the value at all.

    If you still wanted to print k: each time, you can use the above simply with:

    cout << "k: " << vector_dimension_v<T> << endl;
    

    This only works if the type is known to be a vector -- but it could be written using concepts to work with anything following the abstract definition of something like a vector as well.

    If you want this to work with any range-like type, then you could replace the vector-overload with a requires(std::ranges::range<T>) instead, and change the template-specializations for finding the dimension to also use the same. I won't pollute the answer with all this code since it's largely the same as above -- but I'll link to it in action below:

    Live Example