如何初始化 Matrix<T,Rows,Cols> 类型的 constexpr 矩阵?

How to initialise a constexpr matrix of type Matrix<T,Rows,Cols>?

我真的没能总结出问题中的问题。我已经提出了一个类似的问题 。在那里我请求帮助定义 static constexpr 矩阵。解决方案是在模板矩阵列表中添加另一个参数,基本上是 Matrix<T,Rows,Cols,std::make_index_sequence<Rows*Cols>>

我接受了答案,但后来我注意到在这个版本中,我的旧代码无法支持调用以 Matrix<T,Rows,Cols> 作为参数的函数,例如:

foo(Matrix<T,Rows,Cols> & foo){...}

因为缺少第四个隐式参数给我一个编译错误,即 candidate template ignored: could not match '__make_integer_seq' against 'integer_sequence'.

  1. 有人可以向我解释为什么以及我应该怎么做才能解决 这个?我想它是可以修复的,但我无法弄清楚。

然后我发现我可以用不同的方式定义 class,保持 classic 结构 Matrix<T,Rows,Cols>,但仍然能够定义 static constexpr矩阵(我只是在这里添加了必要的内容):

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;
};

因此,使用构造函数中的 static_cast(),我可以在不更改矩阵 class 模板的情况下定义静态矩阵。我可以做一些事情

 static constexpr Matrix< double, 2, 2> A{1.,2.,3.,4.};

但我也可以维护对 foo(Matrix<T,Rows,Cols> & foo){...} 等函数的调用。所以我对这个解决方案没问题。但是后来我尝试创建一个矩阵矩阵,我发现这个 class 版本在这种构造函数中失败了:

Matrix< Matrix<double,1,1>, 2, 2> A{{0.1},{0.1},{0.1},{0.1}};

即使我先初始化元素然后将它们作为参数传递也会成功:

    static constexpr Matrix< double, 1,1> a{{0.1}};
    static constexpr Matrix< Matrix<double,1,1>, 2, 2> A{a,a,a,a};

但是,如果可能的话,我想避免这种情况。

作为一个更清楚的例子,我将给出这个:

Matrix< Matrix<Real,1,1>, 2, 2> A{a,a,a,{0.1}};

这给出了以下编译错误:

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

如果我编写构造函数 Matrix<double,1,1>{0.1} 而不是 {0.1},那么它可以工作,但看起来很可怕。


  1. 为什么我不能简单地构造矩阵的元素 {0.1} 系列?有什么解决方法吗?
  1. 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.

我无法解释。

我可以确认 clang++ 给出了错误,但是 g++ 编译没有问题。我怀疑是 clang++ 错误(或不符合标准),但我不是专家。我打算将问题简化并作为一个单独的问题提出。

如何解决?您可以添加一个继承级别。

您可以创建一个 Matrix_base class 来实现 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
 };

并从 Matrix class 继承它,只有三个模板参数

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
 };

下面是一个完整的例子

Why cannot I simply construct the elements of the matrix by means of the series of {0.1}? Is there any workaround for this?

问题是类型推导。

如果你有构造函数

template<typename...Inputs>
      constexpr Matrix (const Inputs&...vals)
      : values{{ {static_cast<T>(vals)}...}}
      {static_assert(sizeof...(Inputs)==Rows*Cols, "...");}

接收一个可变序列的 Input... 参数,其中编译器 必须 推导 Inputs... 类型从 vals... 值中,您不能将某些东西作为 {0.1}(或者可能是 {0.1, 0.2, 0.3, 0.4})作为希望编译器可以从中推断出类型的值传递。

如果你有原始构造函数则不同

  constexpr Matrix (getType<value_type, Is> ... vals)
     : values_{{vals...}}
   {}

其中 getType<value_type, Is> 变为 value_type。您有一个构造函数,它需要一系列 sizeof...(Is) 个已知类型的元素:value_type。因此无需推导类型:当 Matrix 构造函数期望类型为 Matrix<double, 1u, 1u> 的四个元素时,如果您传递四个参数 {0.1}(或者还有四个 0.1),编译器知道{0.1} 必须用于初始化 Matrix<double, 1u, 1u>.


下面是一个完整的编译C++14的例子

#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);
 }