Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Argument mismatch when there doesn't seem to be any

I'm trying to write a program to find the determinant of an NxN matrix, and in doing so, learning about std::array and templates for the first time.

I came up with this implementation, based on the method of cofactors and minor.

#include <array>
#include <iostream>

template <int N>
using Matrix = std::array<std::array<double, N>, N>;

template <int N>
Matrix<N - 1> subMatrix(Matrix<N> matrix, size_t focusRow, size_t focusColumn) {
    Matrix<N - 1> returnMatrix;

    int subMatrixRow = 0, subMatrixColumn = 0;
    static const int matrixSize = matrix.size();

    for (size_t matrixRow = 0; matrixRow < matrixSize; matrixRow++) {
        for (size_t matrixColumn = 0; matrixColumn < matrixSize; matrixColumn++) {
            if (matrixRow != focusRow && matrixColumn != focusColumn) {
                returnMatrix[subMatrixRow][subMatrixColumn++] = matrix[matrixRow][matrixColumn];

                if (subMatrixColumn == matrixSize - 1) {
                    subMatrixColumn = 0;
                    subMatrixRow++;
                }
            }
        }
    }

    return returnMatrix;
}

template <int N>
double getDeterminant(Matrix<N> matrix) {
    static const int matrixSize = matrix.size();
    double determinant = 0;

    if (matrixSize == 1) {
        determinant = matrix[0][0];
    }

    else if (matrixSize == 2) {
        determinant = (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0]);
    }

    else if (matrixSize > 0) {
        int sign = 1;

        for (size_t dimension = 0; dimension < N; dimension++) {
            determinant += sign * matrix[0][dimension] * getDeterminant(subMatrix(matrix, 0, dimension));
            sign = -sign;
        }
    }

    else {
        throw std::invalid_argument("expected square matrix");
    }

    return determinant;
}

int main(int argc, char const* argv[]) {
    static const int length = 3;

    Matrix<length> matrix = {{{1, 2, 3},
                              {4, 5, 6},
                              {7, 8, 9}}};

    printf("determinant = %.2f\n", getDeterminant(matrix));
}

However, when I run make, I get an error I'm not quite able to resolve. How to proceed?

vscode ➜ /workspaces/c-cpp-mirror/11032023-arrays $ make determinant
g++     determinant.cpp   -o determinant
determinant.cpp: In function ‘int main(int, const char**)’:
determinant.cpp:68:57: error: no matching function for call to ‘getDeterminant(Matrix<3>&)’
   68 |     printf("determinant = %.2f\n", getDeterminant(matrix));
      |                                                         ^
determinant.cpp:31:8: note: candidate: ‘template<int N> double getDeterminant(Matrix<N>)’
   31 | double getDeterminant(Matrix<N> matrix) {
      |        ^~~~~~~~~~~~~~
determinant.cpp:31:8: note:   template argument deduction/substitution failed:
determinant.cpp:68:57: note:   mismatched types ‘int’ and ‘long unsigned int’
   68 |     printf("determinant = %.2f\n", getDeterminant(matrix));
      |                                                         ^
make: *** [<builtin>: determinant] Error 1

Here's a smaller program that still exhibits the same behaviour:

#include <array>
#include <iostream>

template <int N>
using Matrix = std::array<std::array<double, N>, N>;

template <int N>
void printMatrix(Matrix<N> matrix) {}

int main(int argc, char const* argv[]) {
    static const int length = 4;

    Matrix<length> matrix = {{{7, -2, 2, 1},
                              {3, 1, -5, 2},
                              {2, 2, -5, 3},
                              {3, 2, 5, 1}}};

    printMatrix(matrix);
}
like image 289
eccentricOrange Avatar asked Oct 19 '25 00:10

eccentricOrange


1 Answers

There are multiple fundamental issues with the shown code.

The first one is thatstd::array's 2nd template parameter is not an int. It's a std::size_t.

Put this fact in a blender, together with a using alias, swirl it around, and you end up in, basically, asking a suffering C++ compiler to deduce an int from a size_t. It's an apple vs oranges problem.

To fix this first problem, all template template<int N> declarations must be changed to template<size_t n>.

But when this happens your compiler will be grumpy, because you will be instructing your suffering C++ compiler to create a matrix with more values than there are atoms in our shared universe:

    else if (matrixSize > 0) {
        Matrix<N - 1> temp;

When Matrix is 0 this will end up being a pretty large Matrix, won't you say? Even though this branch will not be taken, this still has to compile, and as a template your suffering C++ compiler needs to, basically, evaluate it. The chances of that are not very likely.

So, you need to fix that, and change all of these to if constexpr.

Then, the code will compile.

#include <array>
#include <iostream>

template <std::size_t N>
using Matrix = std::array<std::array<double, N>, N>;

template <std::size_t N>
Matrix<N - 1> subMatrix(Matrix<N> matrix, size_t focusRow, size_t focusColumn) {
    Matrix<N - 1> returnMatrix;

    int subMatrixRow = 0, subMatrixColumn = 0;
    static const int matrixSize = matrix.size();

    for (size_t matrixRow = 0; matrixRow < matrixSize; matrixRow++) {
        for (size_t matrixColumn = 0; matrixColumn < matrixSize; matrixColumn++) {
            if (matrixRow != focusRow && matrixColumn != focusColumn) {
                returnMatrix[subMatrixRow][subMatrixColumn++] = matrix[matrixRow][matrixColumn];

                if (subMatrixColumn == matrixSize - 1) {
                    subMatrixColumn = 0;
                    subMatrixRow++;
                }
            }
        }
    }

    return returnMatrix;
}

template <std::size_t N>
double getDeterminant(Matrix<N> matrix) {
    static const int matrixSize = matrix.size();
    double determinant;

    if constexpr (matrixSize == 1) {
        determinant = matrix[0][0];
    }

    else if constexpr (matrixSize == 2) {
        determinant = (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0]);
    }

    else if constexpr (matrixSize > 0) {
        Matrix<N - 1> temp;
        int sign = 1;

        for (size_t dimension = 0; dimension < N; dimension++) {
            temp = subMatrix(matrix, 0, dimension);
            determinant += sign * matrix[0][dimension] * getDeterminant(temp);
            sign = -sign;
        }
    }

    else {
        throw std::invalid_argument("expected square matrix");
    }

    return determinant;
}

int main(int argc, char const* argv[]) {
    static const int length = 3;

    Matrix<length> matrix = {{{1, 2, 3},
                         {4, 5, 6},
                         {7, 8, 9}}};

    printf("determinant = %.2f\n", getDeterminant(matrix));
}
like image 155
Sam Varshavchik Avatar answered Oct 21 '25 16:10

Sam Varshavchik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!