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>>{}));
Esto nos permite escribir
make_array<int[4][5][6]>
para significar
array<array<array<int, 6>, 5, 4>
.
Explicación:
-
std:rank
da el número de dimensiones de un tipo de matriz. Por lo tanto, paraint[4][5][6]
, devuelve 3. -
Le
make_index_sequence
esto amake_index_sequence
para terminar con un paquete de índices. (0, 1, 2
) -
std::remove_all_extents
nos da el tipo subyacente de la matriz;T[a][b]...[n]
->T
(int
) -
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;
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>())
);
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