c++ stdarray

c++ - Declarando 2(o incluso multidimensionales) std:: arrays elegantemente



stdarray (3)

Estoy usando matrices bidimensionales basadas en std::array .

Básicamente en lugar de:

MyType myarray[X_SIZE][Y_SIZE];

Yo tengo:

std::array<std::array<MyType, Y_SIZE>, X_SIZE> myarray;

Esto funciona perfectamente bien pero IMO la declaración no es muy legible.

¿Hay alguna manera de declarar esto usando un mecanismo inteligente de plantilla de C ++, por lo que la declaración podría verse así?

My2DArray<Mytype, X_SIZE, Y_SIZE> myarray;


Podemos ajustar una de las MyNDArray existentes de MyNDArray / md_array_t para llegar a una interfaz alternativa:

template <typename Arr, std::size_t... Is> constexpr auto make_array_impl(std::index_sequence<Is...>) -> md_array_t<std::remove_all_extents_t<Arr>, std::extent_v<Arr, Is>...>; template <typename Arr> using make_array = decltype(make_array_impl<Arr>( std::make_index_sequence<std::rank_v<Arr>>{}));

Explorador de compiladores

Esto nos permite escribir make_array<int[4][5][6]> para significar array<array<array<int, 6>, 5, 4> .

Explicación:

  1. std:rank da el número de dimensiones de un tipo de matriz. Por lo tanto, para int[4][5][6] , devuelve 3.
  2. Le make_index_sequence esto a make_index_sequence para terminar con un paquete de índices. ( 0, 1, 2 )
  3. std::remove_all_extents nos da el tipo subyacente de la matriz; T[a][b]...[n] -> T ( int )
  4. std::extent nos da la extensión de la dimensión dada. Llamamos a esto para cada índice. ( 4, 5, 6 ).

Al pasarlos a nuestro md_array_t implementado md_array_t , terminamos con md_array_t<int, 4, 5, 6> , que produce lo que queremos.


Si desea solo matrices 2D, es bastante sencillo:

template <class T, std::size_t X, std::size_t Y> using My2DArray = std::array<std::array<T, Y>, X>;

Si desea un mecanismo genérico no limitado a matrices 2D, también puede hacerlo:

template <class T, std::size_t N, std::size_t... Ns> struct AddArray { using type = std::array<typename AddArray<T, Ns...>::type, N>; }; template <class T, std::size_t N> struct AddArray<T, N> { using type = std::array<T, N>; }; template <class T, std::size_t... N> using MyNDArray = typename AddArray<T, N...>::type;

[Ejemplo en vivo]


Una forma algo elegante de implementar esta operación es con una expresión de plegado:

// Some namespace to hide the poorly-constrained template function: namespace array_making { template <std::size_t N> struct array_dim {}; template <typename T, std::size_t N> constexpr auto operator%(array_dim<N>, T const&) -> std::array<T, N>; } template <typename T, std::size_t... Is> using md_array_t = decltype( (array_making::array_dim<Is>{} % ... % std::declval<T>()) );

Compilador Explorer .

Entonces md_array_t<int, 1, 2, 3> es array<array<array<int, 3>, 2>, 1> . Si prefiere el orden opuesto, invierta los parámetros del operator% y los argumentos a la expresión de plegado.

Tenga en cuenta que esto tendrá problemas si el tipo T tiene un operator% sin restricciones operator% en un espacio de nombres asociado (¡limite sus operadores!). Podemos reducir el riesgo de que esto suceda eligiendo operadores poco probables como .* , ->* o %= ; o podemos usar un array_type<T> . Ninguna de las soluciones evita por completo el problema de sobrecargas del operador inadecuadamente restringidas para T