【问题标题】:How to initialise a constexpr matrix of type Matrix<T,Rows,Cols>?如何初始化 Matrix<T,Rows,Cols> 类型的 constexpr 矩阵?
【发布时间】:2019-07-21 07:54:52
【问题描述】:

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

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

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

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

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

然后我发现我可以用不同的方式定义类,保持经典结构Matrix&lt;T,Rows,Cols&gt;,但仍然可以定义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(),我可以在不更改 Matrix 类的模板的情况下定义静态矩阵。我可以做一些事情

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

但我也可以保持对foo(Matrix&lt;T,Rows,Cols&gt; &amp; foo){...} 等函数的调用。所以我对这个解决方案很满意。但是后来我尝试创建一个矩阵矩阵,我发现这个类版本在这种构造函数中失败了:

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&lt;double,1,1&gt;{0.1}而不是{0.1},那么它可以工作,但是看起来很可怕。


  1. 为什么我不能简单地构造矩阵的元素 {0.1}的系列?有什么解决方法吗?

【问题讨论】:

    标签: c++ c++14 variadic-templates template-specialization template-argument-deduction


    【解决方案1】:
    1. 请有人向我解释一下为什么以及我应该怎么做才能解决这个问题?我想它是可以修复的,但我无法弄清楚。

    我无法解释。

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

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

    您可以创建一个Matrix_base 类来实现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 类继承它,只有三个模板参数

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

    下面是一个完整的例子

    为什么我不能简单地通过 {0.1} 的系列来构造矩阵的元素?有什么解决方法吗?

    问题是类型推导。

    如果你有构造函数

    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&lt;value_type, Is&gt; 变为value_type。您有一个构造函数,它需要一系列已知类型的sizeof...(Is) 元素:value_type。所以不必推导任何类型:当Matrix 构造函数需要四个Matrix&lt;double, 1u, 1u&gt; 类型的元素时,如果您传递四个参数{0.1}(或四个0.1),编译器知道必须使用{0.1}初始化一个Matrix&lt;double, 1u, 1u&gt;


    以下是完整的编译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);
     }
    

    【讨论】:

    • 好的,所以我很幸运,使用静态转换,编译器至少能够为更简单的情况制作演绎类型。感谢您的回答。
    • @Garo - 顺便说一句,我添加了一个关于编译错误的new question(仅限clang++);目前没有答案,但在我看来,一个常见的怀疑是clang++错误/不符合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    相关资源
    最近更新 更多