0

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?

MOON
  • 2,516
  • 4
  • 31
  • 49

2 Answers2

2

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.)

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Thanks! Where can I learn more about this stuff? It seems a simple C++ tutorial does not cut it! The error happens if I try to use the instance of the struct inside std::sort. Is that why I need constexpr? because std::sort expects such a thing? – MOON Dec 22 '22 at 22:03
  • 2
    @MOON It needs to be a compile-time known value because you are using it as template argument, meaning the `mat.col_size` inbetween `<` and `>` of `std::array` in `std::array`. It has nothing to do with `std::sort`. – user17732522 Dec 22 '22 at 22:04
  • 2
    @MOON I guess any introduction to non-type template parameters should mention that the corresponding argument must be a constant expression and give a short introduction to what that is if it hasn't done so yet. In fact this needs to be known already when arrays are introduced. The size of an array must also be a constant expression with the exact same meaning as here for the template argument. `int arr[mat.col_size];` is also not legal standard C++ with your code for the same reason. – user17732522 Dec 22 '22 at 22:07
  • 1
    @MOON Also, I don't know what exactly you refer to with "_simple C++ tutorial_", but if you mean the typical tutorial you find online with a Google or Youtube search, then yes, they don't cut it, often even for much more basic stuff. The [recommended books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) are a much better source. But I am not sure whether introductory books will go into that much detail on this, it would depend on the priorities of the author. There are also more advanced recommendations there though. – user17732522 Dec 22 '22 at 22:12
1

You are looking for static constexpr (compile-time constant expression), not const - which might even be something set in, e.g., ctor using run-time values.

template <const int nrow, const int ncol>
struct Mat{
    static constexpr int row_size = nrow;
    static constexpr int col_size = ncol;
    std::array<std::array<float, ncol>, nrow> mat;
};
lorro
  • 10,687
  • 23
  • 36
  • Where does the error come form though? It seems it comes from the std::sort function which expects static constexpr in its comparator. Right? – MOON Dec 22 '22 at 22:00
  • @MOON `error: the value of ‘mat’ is not usable in a constant expression`. The error is that you're using a runtime constant where a compile-time constant is expected (`mat.col_size`). – lorro Dec 22 '22 at 22:02
  • 1
    @MOON It comes from attempting to instantiate the `std::array` template with non-constant template parameters. That is, `std::array` is invalid because `mat.col_size` is not a constant expression. – Miles Budnek Dec 22 '22 at 22:04
  • I think all these comments are wrong. The error comes from the fact that every `Mat` instance has their own `const` `col_size` *variable* internally, which is initialized in their constructors (ergo, not a compile time constant). if you make `col_size` `static` (or `constexpr`, which is identical in this regard), then there's only one compile time constant in the program. – Mooing Duck Dec 22 '22 at 22:20
  • @MooingDuck - hence, these are not compile-time constants. If you look at how the struct is formed, these originate from template arguments, so must be the same for each instance. – lorro Dec 22 '22 at 23:51