Search code examples
c++templatesstructnon-type-template-parameter

Why can't I use a const data member as a template argument?


I have the struct and a template as below:

template <const int nrow, const int ncol>
struct Mat{
    const int row_size = nrow;
    const int col_size = ncol;
    std::array<std::array<float, ncol>, nrow> mat;
};

I want to sort the matrix produced by Mat struct, so I did:

int main(int argc, const char * argv[]) {
    Mat<3, 3> mat;
    mat.mat = {{{1,2,3},{4,5,6},{7,8,9}}};
    std::sort(std::begin(mat.mat), std::end(mat.mat),
    [](std::array<float, mat.col_size>& c1, std::array<float, mat.col_size>& c2){return c1[0]>c2[0];});

}

However, I get this error:

Non-type template argument is not a constant expression

and this expression,mat.col_size , in sort is underlined red in XCode. In the struct I made the col_size a constant but it did not help. However, if I add static keyword before it, then it works fine.

Why was there this error and what static does?


Solution

  • You declared row_size and col_size as non-static data members, so although they are const, they can still be initialized to any value in any given instance of the class. The default member initializer is used only if no other value is given e.g. in an aggregate initialization.

    However you are trying to use these values as template arguments (e.g. in std::array<float, mat.col_size>), which must be known at compile-time. More specifically the template argument must be a "constant expression", so the members must be "usable in constant expressions". So they can't be object-dependent, at least as long as the class object itself is not a compile-time constant, aka "usable in constant expressions" by being declared constexpr, and must be static:

    static const int row_size = nrow;
    static const int col_size = ncol;
    

    For const int with initializer this is sufficient for their values to be considered compile-time constants (aka. "usable in constant expressions"). For non-integral/enumeration types, you will need to replace const with constexpr. (Integral and enumeration types are an exception not requiring constexpr for historical reasons.)