制作模板元函数以在编译时创建单位矩阵

时间:2017-05-20 09:19:00

标签: c++ c++11 templates template-meta-programming

我问自己,如果知道大小,是否可以在编译时创建单位矩阵。到目前为止,我刚刚编写了一个示例,创建了一个固定大小的基于std :: vector的矩阵,例如: 4×4。但是,我不确定如何设置值。我想,我需要递归:/

// Example program
#include <iostream>
#include <string>
#include <vector>

template <class T>
using vec1D = std::vector<T>;

template <class T>
using vec2D = std::vector<std::vector<T>>;

template <class T, int size>
vec2D<T> make_mat() {
  vec2D<T> mat(size, vec1D<T>(size));
  return mat;
}

int main()
{
  vec2D<float> unit = make_mat<float, 4>();
  std::cout 
  << unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
  << unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
  << unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
  << unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}

2 个答案:

答案 0 :(得分:3)

  

我问自己,如果知道大小,是否有可能在编译时创建一个单位矩阵。

如果你的矩阵是基于std::vector,我不这么认为。

但是,如果它基于std::array并且您希望将其初始化为零(如您的示例中),那么:它是可能的。

// Example program
#include <iostream>
#include <array>

template <typename T, std::size_t D1, std::size_t D2 = D1>
constexpr std::array<std::array<T, D2>, D1> doA ()
 { return { }; }

int main()
{
  constexpr auto unit = doA<float, 4U>();

  std::cout 
  << unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
  << unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
  << unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
  << unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}

但我认为这不是一个好主意:请记住std::array使用堆栈,而不是堆。所以这个解决方案只适用于很少(很少)的矩阵。

---编辑---

OP(正确)观察

  

问题是是否可以将矩阵作为单位矩阵初始化:100 010 001

是的,单位矩阵也可以;有点复杂但可能。

// Example program
#include <iostream>
#include <utility>
#include <array>

template <typename T, std::size_t I, std::size_t ... Is>
constexpr auto doU_helper2 (std::index_sequence<Is...> const &)
 { return std::array<T, sizeof...(Is)>
    { { (Is == I ? T{1} : T{0})... } }; }

template <typename T, std::size_t ... Is>
constexpr auto doU_helper1 (std::index_sequence<Is...> const & is)
 { return std::array<std::array<T, sizeof...(Is)>, sizeof...(Is)>
    { { doU_helper2<T, Is>(is)... } }; }

template <typename T, std::size_t Dim>
constexpr auto doU ()
 { return doU_helper1<T>(std::make_index_sequence<Dim>{}); }

int main()
{
  constexpr auto unit = doU<float, 4U>();

  std::cout 
  << unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
  << unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
  << unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
  << unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}

请记住此代码使用的是std::make_index_sequence{}std::index_sequence,这是C ++ 14的功能。

如果你需要一个C ++ 11解决方案,那么创建一个副本并不困难;问你是否需要它(在某个地方我已经完成;我只能检索)。

答案 1 :(得分:2)

您无法在编译时进行动态分配。

您可以在运行时执行此操作。简单的方法是使用循环。

如果你想完全避免循环:

template<std::size_t...Is>
auto index_over(std::index_sequence<Is...>){
  return [](auto&&f)->decltype(auto){
    return decltype(f)(f)(std::integral_constant<std::size_t,Is>{}...);
  };
}
template<std::size_t N>
auto index_upto(std::integral_constant<std::size_t,N> ={}){
  return index_over(std::make_index_sequence<N>{});
}

template <class T>
using vec1D = std::vector<T>;

template <class T>
using vec2D = std::vector<std::vector<T>>;

template<std::size_t N, std::size_t I,class T>
vec1D<T> make_row( T const& base, T const& diag ){
  return index_upto<I>()([&](auto...Before){
    return index_upto<N-I-1>()([&](auto...After)->vec1D<T>{
      return {
        (void(Before), base)...,
        diag,
        (void(After), base)...
      };
    });
  });
}

template<std::size_t N, class T>
vec2D<T> make_mat( T const& base, T const& diag){
  return index_upto<N>()([&}(auto...Rs)->vec2D<T>{
    return {
      make_row<N, Rs>(base, diag)...
    };
  });
}

可能包含拼写错误(写在手机上,未经过测试)。有时会使用有效C ++但g ++ choke的构造;铿锵会理解它。 C ++ 14

索引反而避免必须编写一堆辅助函数来扩展索引参数包;他们让你在lambda中进行扩展。

如果您的编译器抱怨在编译时使用Rs,请替换为decltype(Rs)::value - 您的编译器评估失败{/ 1}}。

在C ++ 11中执行此操作很烦人。我可能会使用帮助程序类型来执行包扩展,每个函数我上面写的一个。您还想从C ++ 14重写索引序列。非常好,只是做循环。

constexpr

生成auto u = make_mat<4>( 0, 1 ); 的对角矩阵,大小为4x4。

请注意,此技术使用自由int以及大小调整和类型,可以使用std数组。可悲的是,在C ++ 17之前,lambda不能成为constexpr。

循环解决方案也适用于C ++ 14之后的constexpr矩阵;你可以改变constexpr中的本地数据。