Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler complain about this not being a constexpr?

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!

like image 677
BigONotation Avatar asked Jun 29 '16 15:06

BigONotation


People also ask

Is constexpr always evaluated at compile time?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.

Why constexpr instead of define?

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

Can constexpr be changed?

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.

Does constexpr improve performance?

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.


1 Answers

[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:

 
like image 121
Piotr Skotnicki Avatar answered Dec 01 '22 00:12

Piotr Skotnicki