I was not really able to summarize the problem in the question. I already made a similar question here.There I asked help for defining static constexpr
matrices. The solution was to add another argument to the list of the template matrix, basically Matrix<T,Rows,Cols,std::make_index_sequence<Rows*Cols>>
.
I accepted the answer, but later I noticed that with this version, my older code was not able to support the call of functions which have as argument a Matrix<T,Rows,Cols>
, for example as:
foo(Matrix<T,Rows,Cols> & foo){...}
since the absence of the fourth implicit parameter was giving me a compile error, i.e. candidate template ignored: could not match '__make_integer_seq' against 'integer_sequence'
.
Then I found out that I can define the class in a different manner, maintaining the classic structure Matrix<T,Rows,Cols>
, but still being able to define static constexpr
matrices (I just added what is necessary here):
template<typename T, Integer Rows_, Integer Cols_>
class Matrix {
public:
static constexpr Integer Rows=Rows_;
static constexpr Integer Cols=Cols_;
using type= Matrix<T,Rows,Cols>;
using subtype=T;
~Matrix()=default;
Matrix() {}
template<typename...Inputs>
constexpr Matrix (const Inputs&...vals)
: values{{ {static_cast<T>(vals)}...}}
{static_assert(sizeof...(Inputs)==Rows*Cols, "...");}
private:
std::array<T, Rows * Cols> values;
};
So with the static_cast()
in the constructor I can define static matrices without changing the template of the Matrix class. I can do something as
static constexpr Matrix< double, 2, 2> A{1.,2.,3.,4.};
but I can also maintain the call to functions like foo(Matrix<T,Rows,Cols> & foo){...}
. So I was ok with this solution. But then I tried to create a matrix of matrices and I found out that this class version fails in the constructor of this kind:
Matrix< Matrix<double,1,1>, 2, 2> A{{0.1},{0.1},{0.1},{0.1}};
even though it succeeds if I first initialize the elements and then I pass them as argument:
static constexpr Matrix< double, 1,1> a{{0.1}};
static constexpr Matrix< Matrix<double,1,1>, 2, 2> A{a,a,a,a};
However, I would like to avoid this, if possible.
As a more clarifying example I will give this:
Matrix< Matrix<Real,1,1>, 2, 2> A{a,a,a,{0.1}};
which gives the following compile error:
candidate template ignored: substitution failure : deduced incomplete pack <Matrix<double, 1, 1>, Matrix<double, 1, 1>, Matrix<double, 1, 1>,
(no value)> for template parameter 'Inputs'``` (so ```{0.1}``` is no value).
If instead of {0.1}
I write the constructor Matrix<double,1,1>{0.1}
, then it works, but it is horrible to look at.
{0.1}
? Is there any workaround for this?
- Can someone, please, explain to me why and what should I do to fix this? I guess it is fixable but I was not able to figure it out.
I can't explain.
I can confirm that clang++ gives the error but g++ compile without problem. I suspect a clang++ bug (or a not standard compliance) but I'm not an expert. I intend to simplify the problem and propose it as a separate question.
How to fix? You can add a level of inheritance.
You can create a Matrix_base
class that make the trick of std::make_index_sequence
/std::index_sequence
template <typename, typename>
class Matrix_base;
template <typename T, std::size_t ... Is>
class Matrix_base<T, std::index_sequence<Is...>>
{
// values_ and constructors
};
and inherit it from a Matrix
class whit only three template parameter
template <typename T, std::size_t NR, std::size_t NC>
class Matrix : public Matrix_base<T, std::make_index_sequence<NR*NC>>
{
public:
using value_type = T;
using MB = Matrix_base<T, std::make_index_sequence<NR*NC>>;
using MB::MB;
using MB::values_;
// other methods
};
A complete example follows
Why cannot I simply construct the elements of the matrix by means of the series of {0.1}? Is there any workaround for this?
The problem is the type deduction.
If you have a constructor
template<typename...Inputs>
constexpr Matrix (const Inputs&...vals)
: values{{ {static_cast<T>(vals)}...}}
{static_assert(sizeof...(Inputs)==Rows*Cols, "...");}
that receive a variadic sequence of Input...
argument, where the compiler must deduce the Inputs...
types from the vals...
values, you can't pass something as {0.1}
(or maybe {0.1, 0.2, 0.3, 0.4}
) as a value hoping that the compiler can deduce a type from it.
Different if you have the original constructor
constexpr Matrix (getType<value_type, Is> ... vals)
: values_{{vals...}}
{}
where getType<value_type, Is>
become value_type
. You have a constructor that expect a sequence of sizeof...(Is)
elements of a known type: value_type
. So no type has to be deduced: when the Matrix
constructor expects four element of type Matrix<double, 1u, 1u>
, if you pass four argument {0.1}
(or also four 0.1
) the compiler know that {0.1}
must be used to initialize a Matrix<double, 1u, 1u>
.
The following is a full compiling C++14 example
#include <array>
#include <type_traits>
template <typename T, std::size_t>
using getType = T;
template <typename, typename>
class Matrix_base;
template <typename T, std::size_t ... Is>
class Matrix_base<T, std::index_sequence<Is...>>
{
protected:
std::array<T, sizeof...(Is)> values_{};
public:
constexpr Matrix_base (getType<T, Is> ... vals)
: values_{{vals...}}
{}
constexpr Matrix_base (std::array<T, sizeof...(Is)> const & a)
: values_{a}
{}
constexpr Matrix_base (std::array<T, sizeof...(Is)> && a)
: values_{std::move(a)}
{}
constexpr Matrix_base () = default;
~Matrix_base() = default;
constexpr Matrix_base (Matrix_base const &) = default;
constexpr Matrix_base (Matrix_base &&) = default;
constexpr Matrix_base & operator= (Matrix_base const &) = default;
constexpr Matrix_base & operator= (Matrix_base &&) = default;
};
template <typename T, std::size_t NR, std::size_t NC>
class Matrix : public Matrix_base<T, std::make_index_sequence<NR*NC>>
{
public:
using value_type = T;
using MB = Matrix_base<T, std::make_index_sequence<NR*NC>>;
using MB::MB;
using MB::values_;
constexpr T const & operator() (std::size_t r, std::size_t c) const
{ return values_[r*NC+c]; }
T & operator() (std::size_t r, std::size_t c)
{ return values_[r*NC+c]; }
constexpr std::size_t rows () const
{ return NR; }
constexpr std::size_t columns () const
{ return NC; }
};
template <typename T, std::size_t Dim1, std::size_t Dim2>
void foo (Matrix<T, Dim1, Dim2> const &)
{ }
int main()
{
static constexpr Matrix<double,2,2> staticmat{0.1,0.2,0.3,0.4};
Matrix<Matrix<double,1,1>, 2, 2> a{{0.1}, {0.1}, {0.1}, {0.1}};
Matrix<Matrix<double,1,1>, 2, 2> b{0.1, 0.1, 0.1, 0.1};
foo(staticmat);
foo(a);
foo(b);
}