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!
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.
#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.
A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.
Using constexpr to Improve Security, Performance and Encapsulation in C++ constexpr is a new C++11 keyword that rids you of the need to create macros and hardcoded literals. It also guarantees, under certain conditions, that objects undergo static initialization.
[basic.types]/p10 states that:
A type is a literal type if it is:
possibly cv-qualified
void
; ora 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 default
ed, or removed altogether, so:
~Matrix() = default;
or:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With