As noted in comments, C++ does not support variable-length arrays (VLAs).   C did from the 1999 standard, but that became optional in C11.   In combination, those factors are relevant to why gcc (depending on version) accepts your code, but g++ does not.
In C (and C++ if writing in a C style <blech!>), an alternative is to pass a single-dimensional array (with contiguous elements) to a function that accepts a pointer and use an indexing scheme to access elements.   For example, assuming row-major ordering;
void print_mat(const int nr, const int nc, const float *x)
{
    for (int ir=0; ir<nr; ir++)
    {
        int row_start = ir * nc;
        for (int ic=0; ic<nc; ic++)
        {
            printf(" %f",x[row_start + ic]);
        }
        printf("\n");
    }
}
In C++, one can use - depending on which (if any) dimensions are known at compile time;
- std::array<std::array<float, nc>, nr>(if array dimensions- ncand- nrare both fixed at compile time);
- std::vector<std::vector<float> >(if neither dimension is known
until run time).   Bear in mind that individual- std::vector<float>s
in a- std::vector<std::vector<float> >CAN have different dimensions.  Your caller will need to ensure dimensions are the same for all contained- std::vector<float>s and/or your function will need to check sizes.
If nc is fixed at compile time but nr is not, you can use std::vector<std::array<float, nc> >.    If nr is fixed at compile time, but nc is not, you can use std::array<std::vector<float>, nr>.
If you must pass the entire vector/array, usually better to pass it by reference than by value.   For example;
void print_mat(const std::array<std::array<float, nc>, nr> &)
{
     // definition
}
or (if you need to pass around some arrays of different dimensions) create a family of such functions
template<int nc, int nr>
void print_mat(const std::array<std::array<float, nc>, nr> &)
{
     // definition
}
Personally, I would not actually pass arrays or vectors around.   I'd use iterators, such as;
template<class NestedIterator>
void print_mat(NestedIterator row, NestedIterator end_row)
{
     while (row != end_row)
     {
           auto col = std::begin(*row);     // Assuming C++11 and later
           auto col_end = std::end(*row);
           while (col != col_end)
           {
               std::cout << ' ' << *col;
               ++col;
           }
           std::cout << '\n';   // or std::endl
           ++row;
     }
}
This function assumes begin and end iterators from a container that contains (nested) containers (so passing iterators from a std::vector<float> will be a diagnosable error).   It works for any type of element (e.g. is not limited to float in your case), that can be streamed to a std::ostream.
I've assumed row-major ordering in the above.  The adjustments for column-major ordering are trivial.