9

I am trying to learn a bit more on how to use C++ constant expressions in practice and created the following Matrix class template for illustration purposes:

#include <array> template <typename T, int numrows, int numcols> class Matrix{ public: using value_type = T; constexpr Matrix() : {} ~Matrix(){} constexpr Matrix(const std::array<T, numrows*numcols>& a) : values_(a){} constexpr Matrix(const Matrix& other) : values_(other.values_){ } constexpr const T& operator()(int row, int col) const { return values_[row*numcols+col]; } T& operator()(int row, int col){ return values_[row*numcols+col]; } constexpr int rows() const { return numrows; } constexpr int columns() const { return numcols; } private: std::array<T, numrows*numcols> values_{}; }; 

The idea is to have a simple Matrix class, which I can use for small matrices to evaluate Matrix expressions at compile time (note that I have not yet implemented the usual Matrix operators for addition and multiplication).

When I try to initialize a Matrix instance as follows:

constexpr std::array<double, 4> a = {1,1,1,1}; constexpr Matrix<double, 2, 2> m(a); 

I am getting the following error from the compiler (MS Visual C++ 14):

error: C2127: 'm': illegal initialization of 'constexpr' entity with a non-constant expression 

Note sure what I am doing wrong...any help to make this work would be greatly appreciated!

5
  • Maybe std::array doesn't have a constexpr copy constructor? Commented Jun 29, 2016 at 15:17
  • 2
    Remove the destructor's definition Commented Jun 29, 2016 at 15:20
  • 2
    As a sidenote, there is no need to store numrows_ and numcols_ as member variables. Since you have the values as template parameters already, just return those. Commented Jun 29, 2016 at 15:23
  • 1
    @Piotr Skotnicki yes that did the trick. Thanks! If you post this as an answer I will accept it. But I don't understand why defining a destructor triggers the error? Commented Jun 29, 2016 at 15:26
  • @nasser-sh as suggested I cleaned up the code by removing unnecessary member variables for row and column size Commented Jun 29, 2016 at 20:39

1 Answer 1

15

[basic.types]/p10 states that:

A type is a literal type if it is:

  • possibly cv-qualified void; or

  • a scalar type; or

  • a reference type; or

  • an array of literal type; or

  • a possibly cv-qualified class type (Clause [class]) that has all of the following properties:

    • it has a trivial destructor,

    • it is either a closure type ([expr.prim.lambda]), an aggregate type ([dcl.init.aggr]), or has at least one constexpr constructor or constructor template (possibly inherited ([namespace.udecl]) from a base class) that is not a copy or move constructor,

    • if it is a union, at least one of its non-static data members is of non-volatile literal type, and

    • if it is not a union, all of its non-static data members and base classes are of non-volatile literal types.

where [class.dtor]/p5 says that:

A destructor is trivial if it is not user-provided and if:

(5.4) — the destructor is not virtual,

(5.5) — all of the direct base classes of its class have trivial destructors, and

(5.6) — for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

Otherwise, the destructor is non-trivial.

In other words, to declare a constexpr instance of Matrix, it must be a literal type, and to be a literal type, its destructor must be either defaulted, or removed altogether, so:

~Matrix() = default; 

or:

 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for all the clarifications!
I like the or. :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.