Tengo una función que toma una std::vector
dimensión multidimensional y requiere que se pase la profundidad (o el número de dimensiones) como parámetro de plantilla. En lugar de codificar este valor, me gustaría escribir una constexpr
función que tome std::vector
y devuelva la profundidad como unsigned integer
valor.
Por ejemplo:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
Sin embargo, esto debe hacerse en tiempo de compilación porque esta profundidad se pasará a la función de plantilla como un parámetro de plantilla:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
¿Hay alguna forma de hacer esto?
std::vector
es algo en tiempo de ejecución, no en tiempo de compilación. Si desea un contenedor de tamaño de tiempo de compilación, busquestd::array
. También; recuerde queconstexpr
solo significa " puede ser evaluado en tiempo de compilación" - no hay promesa de que lo será . Se puede evaluar en tiempo de ejecución.std::vector
s están anidados entre sí. Por ejemplostd::vector<std::vector<int>> v;
, con ,GetDepth(v);
devolvería 2 ya que es un vector bidimensional. El tamaño es irrelevante.vector
no siempre es la mejor manera de hacer las cosas. La indexación manual en 2D o 3D de un solo vector plano puede ser más eficiente, dependiendo del caso de uso. (Solo matemáticas enteras en lugar de perseguir punteros desde los niveles exteriores.)rank
para esta consulta en tipos de matriz (de acuerdo con la nomenclatura matemática para tensores). Quizás esa sea una palabra mejor aquí que "profundidad".Respuestas:
Un clásico problema de plantillas. Aquí hay una solución simple como la biblioteca estándar de C ++. La idea básica es tener una plantilla recursiva que contará una por una cada dimensión, con un caso base de 0 para cualquier tipo que no sea un vector.
Entonces puedes usarlo así:
Editar:
Ok, he terminado la implementación general para cualquier tipo de contenedor. Tenga en cuenta que definí un tipo de contenedor como cualquier cosa que tenga un tipo de iterador bien formado según la expresión
begin(t)
dondestd::begin
se importa para la búsqueda de ADL yt
es un valor de tipo lT
.Aquí está mi código junto con comentarios para explicar por qué funcionan las cosas y los casos de prueba que utilicé. Tenga en cuenta que esto requiere C ++ 17 para compilar.
fuente
std::vector<T>
especialización y cambiarla a otro tipo de contenedor. Lo único que necesita es el caso base 0 para cualquier tipo para el que no se especialiceSuponiendo que un contenedor es cualquier tipo que tenga
value_type
yiterator
tipos de miembros (los contenedores de biblioteca estándar satisfacen este requisito) o una matriz de estilo C, podemos generalizar fácilmente la solución de Cruz Jean :Los tipos de contenedores pueden restringirse aún más si es necesario.
fuente
Puede definir la siguiente plantilla de clase
vector_depth<>
que coincida con cualquier tipo:Esta plantilla principal corresponde al caso base que finaliza la recursión. Luego, defina su especialización correspondiente para
std::vector<T>
:Esta especialización coincide con
std::vector<T>
y corresponde al caso recursivo.Finalmente, defina la plantilla de función
GetDepth()
, que recurre a la plantilla de clase anterior:Ejemplo:
La salida de este programa es:
fuente
std::vector
, pero por ejemploGetDepth(v)
, dondev
seint
no se compilará. Sería mejor tenerGetDepth(const volatile T&)
y regresarvector_depth<T>::value
.volatile
solo deja que cubra más cosas, siendo el máximo cv calificado