将一种类型的constexpr n维数组转换为另一种类型的constexpr n维数组

时间:2017-11-17 07:34:22

标签: c++11 c++14 constexpr

使用C ++ 11或C ++ 14:

我有一个:

constexpr double [2][3][4] = some value;

我想要constexpr int [2][3][4]

(实际上我会想要constexpr my_type [2][3][4],但是一旦int工作,这应该是可以解决的。)

我想出的唯一解决方案是一个容器类,operator[]被重载,所以它的行为就像一个数组。有没有办法保持标准数组语法?或者可能std::array<std::array...>>而不创建新类?

我不希望输入为std::array - 这是系统的约束。输入必须与C兼容。转换后的数组可以是C ++。

谢谢, 诺亚

3 个答案:

答案 0 :(得分:2)

如果您接受constexpr std::array<std::array<int, 4U>, 3U>, 2U>,那么......使用 SFINAE 标记调度,std::rankdecltype()auto作为返回类型和std::index_sequence(所以C ++ 14解决方案)你可以使用getArray()和他的助手getArrayH()进行乒乓球比赛,如下所示

注意:回答修改(改进,恕我直言)从朱利叶斯的例子中获取灵感(谢谢!)

#include <array>
#include <iostream>
#include <type_traits>

template <typename T>
std::integral_constant<bool, std::rank<T>::value != 0U> isArray ();

template <typename targetT, typename origT, std::size_t Dim>
constexpr auto getArray (origT(&)[Dim]);

template <typename targetT, typename arrT, std::size_t ... Is>
constexpr auto getArrayH (arrT const & arr,
                          std::false_type const &,
                          std::index_sequence<Is...> const &)
 { return std::array<targetT, sizeof...(Is)>{ { targetT(arr[Is])... } }; }

template <typename targetT, typename arrT, std::size_t ... Is>
constexpr auto getArrayH (arrT const & arr,
                          std::true_type const &,
                          std::index_sequence<Is...> const &)
 { return std::array<decltype(getArray<targetT>(arr[0])), sizeof...(Is)>
           { { getArray<targetT>(arr[Is])... } }; }

template <typename targetT, typename origT, std::size_t Dim>
constexpr auto getArray (origT(&arr)[Dim])
 { return getArrayH<targetT>(arr, decltype(isArray<origT>()){},
                             std::make_index_sequence<Dim>{}); }

int main ()
 {
   constexpr double ad3[2][3][4]
    { { { 1.0, 2.0, 3.0, 4.0 },
        { 2.1, 3.1, 4.1, 5.1 },
        { 3.2, 4.2, 5.2, 6.2 } },
      { { 6.3, 5.3, 4.3, 3.3 },
        { 5.4, 4.4, 3.4, 2.4 },
        { 4.5, 3.5, 2.5, 1.5 } } };

   for ( auto const & ad2 : ad3 )
      for ( auto const & ad1 : ad2 )
         for ( auto const & ad0 : ad1 )
            std::cout << ad0 << ' ';

   std::cout << std::endl;

   constexpr auto ai3 = getArray<int>(ad3);

   static_assert(std::is_same<decltype(ai3),
      std::array<std::array<std::array<int, 4U>, 3U>, 2U> const>{}, "!");

   for ( auto const & ai2 : ai3 )
      for ( auto const & ai1 : ai2 )
         for ( auto const & ai0 : ai1 )
            std::cout << ai0 << ' ';

   std::cout << std::endl;
 }

答案 1 :(得分:1)

有一种方法可以通过逐步构建来实现此目的。也就是说,首先解决1D阵列的转换问题。然后基于该解决方案进行2D阵列的转换等。

template<typename U, typename T, std::size_t N, std::size_t... I>
constexpr
std::array<U, N>
convert1DArrayImpl(std::array<T, N> const &a, std::index_sequence<I...>) {
  return {static_cast<U>(a[I])...};
}

template<typename U, typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr
std::array<U, N>
convert1DArray(const std::array<T, N>& a) {
  return convert1DArrayImpl<U>(a, Indices{});
}

template<typename U, typename T, std::size_t N, std::size_t M, std::size_t... IN>
constexpr
std::array<std::array<U, N>, M>
convert2DArrayImpl(std::array<std::array<T, N>, M> a, std::index_sequence<IN...>) {   
  return {convert1DArray<U>(a[IN])...};
}

template<typename U, typename T, std::size_t N, std::size_t M, typename IndicesM = std::make_index_sequence<M>>
constexpr
std::array<std::array<U, N>, M>
convert2DArray(const std::array<std::array<T, N>, M>& a) {
  return convert2DArrayImpl<U>(a, IndicesM{});
}

template<typename U, typename T, std::size_t N, std::size_t M, std::size_t K, std::size_t... IN>
constexpr
std::array<std::array<std::array<U, N>, M>, K>
convert3DArrayImpl(std::array<std::array<std::array<T, N>, M>, K> a, std::index_sequence<IN...>) {   
  return {convert2DArray<U>(a[IN])...};
}

template<typename U, typename T, std::size_t N, std::size_t M, std::size_t K, typename IndicesK = std::make_index_sequence<K>>
constexpr
std::array<std::array<std::array<U, N>, M>, K>
convert3DArray(const std::array<std::array<std::array<T, N>, M>, K>& a) {
  return convert3DArrayImpl<U>(a, IndicesK{});
}

Live Demo

答案 2 :(得分:0)

不幸的是,std::array::operator[] is not constexpr before C++17。因此,您可能需要提出自己的样板。

这是C ++ 17中的一个例子。如果你导出&#34; indices trick&#34;进入一个单独的函数(而不是lambda)并实现自己的std::array版本,那么这种方法也适用于C ++ 14。

https://godbolt.org/g/FayqEn

#include <array>
#include <iostream>
#include <utility>

////////////////////////////////////////////////////////////////////////////////

template<size_t... is, class F>
constexpr decltype(auto) indexer(std::index_sequence<is...>, F f) {
  return f(std::integral_constant<std::size_t, is>{}...);
}

template<size_t N_, class F>
constexpr decltype(auto) indexer(F f) {
  constexpr size_t max_index_length = 4096;
  constexpr size_t N = std::min(N_, max_index_length);
  static_assert(N == N_, "");
  return indexer(std::make_index_sequence<N>{}, f);
}

////////////////////////////////////////////////////////////////////////////////

template<class T>
constexpr auto make_array(T val) {
  return val;
}

template<class NestedArrays, std::size_t N>
constexpr auto make_array(NestedArrays(&arr)[N]) {
  using NestedStdArrays = decltype(make_array(arr[0]));
  return indexer<N>([=] (auto... ns) {
    return std::array<NestedStdArrays, N>{
      make_array(arr[ns])...
    };
  });
}

////////////////////////////////////////////////////////////////////////////////

int main() {
  constexpr int input_from_c[1][3][2]{
    { {0, 10}, {20, 30}, {40, 50} }
  };

  constexpr auto nested_std_arrays = make_array(input_from_c);
  using NestedStdArrays = std::decay_t<decltype(nested_std_arrays)>;

  static_assert(
    std::is_same<
      NestedStdArrays,
      std::array<std::array<std::array<int, 2>, 3>, 1>
    >{}, ""
  );

  static_assert(0 == nested_std_arrays[0][0][0], "");
  static_assert(10 == nested_std_arrays[0][0][1], "");
  static_assert(20 == nested_std_arrays[0][1][0], "");
  static_assert(30 == nested_std_arrays[0][1][1], "");
  static_assert(40 == nested_std_arrays[0][2][0], "");
  static_assert(50 == nested_std_arrays[0][2][1], "");

  return 0;
}