1

hello i want to find a way to replace auto keyword in the following code.

#include <iostream>

using namespace std;

void printMatrix(const auto & matrix) {
    /* print matrix using range-based for */
}

int main() {
    int matrix[][3] = {{}, {}, {}};
    int matrix2[][6] = {{}, {}, {}};
    printMatrix(matrix);
    printMatrix(matrix2);
    return 0;
}

what should i use to replace auto in const auto & matrix. i can use pointers but the problem is i have to pass the rows and columns size. the upper code is working but i want to know how the auto keyword handle this.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • related: https://stackoverflow.com/a/62201889/4117728 – 463035818_is_not_an_ai Jan 12 '23 at 12:44
  • Most important question: Why are you using C-style arrays, instead of `std::array`, `std::vector`, or e.g. [Boost.uBLAS' matrix type](https://www.boost.org/doc/libs/1_81_0/libs/numeric/ublas/doc/matrix.html#1Matrix)? If those are an option (and why should they not?), your C problem of having to pass size "magically" goes away. – DevSolar Jan 12 '23 at 12:55

4 Answers4

4

This function declaration

void printMatrix(const auto & matrix) {
    /* print matrix using range-based for */
}

declares a template function.

Instead you could write for example

template <typename T, size_t M, size_t N>
void printMatrix(const T ( & matrix)[M][N]) {
    /* print matrix using range-based for */
}

and the function is called as the previous function

printMatrix(matrix);
printMatrix(matrix2);

As the element type of the arrays is known then you can also write

template <size_t M, size_t N>
void printMatrix(const int ( & matrix)[M][N]) {
    /* print matrix using range-based for */
}

//...

printMatrix(matrix);
printMatrix(matrix2);

Within the function you can use the values M and N in nested for loops to output the arrays as for example

for ( size_t i = 0; i < M; i++ )
{
    for ( size_t j = 0; j < N; j++ )
    {
        std::cout << matrix[i][j] << ' ';
    }
    std::cout << '\n';
}

Or you can use range-based for loop

for ( const auto &row : matrix )
{
    for ( const auto &item : row )
    {
        std::cout << item << ' ';
    }
    std::cout << '\n';
}

You could do the same using the initial function like for example

#include <iterator>

//...

void printMatrix(const auto & matrix) {
    for ( size_t i = 0, m = std::size( matrix ); i < m; i++ )
    {
        for ( size_t j = 0, n = std::size( matrix[i] ); j < n; j++ )
        {
            std::cout << matrix[i][j] << ' ';
        }
        std::cout << '\n';
    }
}

Here is a demonstration program.

#include <iostream>
#include <iterator>

std::ostream & printMatrix( const auto &matrix, std::ostream &os = std::cout )
{
    for (size_t i = 0, m = std::size( matrix ); i < m; i++)
    {
        for (size_t j = 0, n = std::size( matrix[i] ); j < n; j++)
        {
            os << matrix[i][j] << ' ';
        }
        os << '\n';
    }

    return os;
}

int main()
{
    int matrix[][3] = 
    { 
        { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } 
    };

    int matrix2[][6] = 
    { 
        { 0, 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0, 1 }, { 2, 3, 4, 5, 6, 7 } 
    };

    printMatrix( matrix ) << '\n';
    printMatrix( matrix2 ) << '\n';
}

The program output is

0 1 2
3 4 5
6 7 8

0 1 2 3 4 5
6 7 8 9 0 1
2 3 4 5 6 7
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Re-declare the print function to allow only a function parameter that is a 2D-array by reference, for a parameterized element type:

#include <cstddef>  // std::size_t

template<typename T, std::size_t num_rows, std::size_t num_cols>
void printMatrix(T const (&mat)[num_rows][num_cols]) {
    /* print matrix using range-based for */
}


// ....
printMatrix(matrix);  // template arguments inferred as <int, 3, 3>
printMatrix(matrix2); // template arguments inferred as <int, 3, 6>

This is essentially a more specialized version as compared to an overload using a single type template parameter (as in OP's example, via auto/abbreviated function template with a single invented type template parameter).

dfrib
  • 70,367
  • 12
  • 127
  • 192
1

A good replacement can be with a concept.

From here:

template<typename T>
concept nested_range = std::ranges::range<T> && std::ranges::range<std::ranges::range_value_t<T>>;

So

template <nested_range Matrix>
void printMatrix(const Matrix & matrix) {
  for (const auto& line: matrix) {
      for (const auto& x: line) {
          std::cout << x << " ";
      }
      std::cout << "\n";
  }
}

This way, it is shown in the code that only an iterable (concept range) containing an iterable (e.g. 2D-array) is expected and a reasonable error message will be given for wrong parameters.

Live example with more than enough includes.

stefaanv
  • 14,072
  • 2
  • 31
  • 53
0

You can replace auto with a template parameter. That's how it was done before C++ 14.

#include <iostream>

using namespace std;

template <typename Matrix>
void printMatrix(const Matrix & matrix) {
    /* print matrix using range-based for */
}

int main() {
    int matrix[][3] = {{}, {}, {}};
    int matrix2[][6] = {{}, {}, {}};
    printMatrix(matrix);
    printMatrix(matrix2);
    return 0;
}
Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • "i can use pointers but the problem is i have to pass the rows and columns size." with your approach the size is not readily available. Its not really better than passing a pointer + sizes – 463035818_is_not_an_ai Jan 12 '23 at 12:43
  • Yes, because you can't iterate over elements referenced by a pointer without the size of the array. OP states in their comment "print matrix using range-based for" which works here. https://gcc.godbolt.org/z/7TcjKW3Kq `auto` is replaced and range based loop works. What exactly did I miss? – Simon Kraemer Jan 13 '23 at 08:01
  • @463035818_is_not_a_number You don't need the sizes for range-based for loops. – Simon Kraemer Jan 13 '23 at 08:06