Eu tenho uma função que tem uma std::vector
dimensão multidimensional e requer que a profundidade (ou o número de dimensões) seja passada como um parâmetro de modelo. Em vez de codificar esse valor, eu gostaria de escrever uma constexpr
função que aceite std::vector
e retorne a profundidade como um unsigned integer
valor.
Por exemplo:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
Isso precisa ser feito em tempo de compilação, porque essa profundidade será passada para a função de modelo como um parâmetro de modelo:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
Há alguma maneira de fazer isso?
std::vector
é uma coisa em tempo de execução, não em tempo de compilação. Se você deseja um contêiner de tamanho em tempo de compilação, consultestd::array
. Além disso; lembre-se de queconstexpr
apenas significa " pode ser avaliado em tempo de compilação" - não há promessa de que será . Pode ser avaliado em tempo de execução.std::vector
s estão aninhados um no outro. Por exemplostd::vector<std::vector<int>> v;
,GetDepth(v);
retornaria 2, pois é um vetor bidimensional. O tamanho é irrelevante.vector
nem sempre é a melhor maneira de fazer as coisas. A indexação manual 2d ou 3d de um único vetor plano pode ser mais eficiente, dependendo do caso de uso. (Apenas matemática inteiro em vez de ponteiro-perseguição dos níveis exteriores.)rank
para esta consulta em tipos de matriz (de acordo com a nomenclatura matemática para tensores). Talvez essa seja uma palavra melhor aqui do que "profundidade".Respostas:
Um problema clássico de modelagem. Aqui está uma solução simples, como a biblioteca padrão C ++. A idéia básica é ter um modelo recursivo que contará um por um cada dimensão, com um caso base de 0 para qualquer tipo que não seja um vetor.
Então você poderia usá-lo da seguinte maneira:
Editar:
Ok, eu terminei a implementação geral para qualquer tipo de contêiner. Note-se que I definido um tipo de recipiente como qualquer coisa que tenha um tipo de iteração bem formada de acordo com a express
begin(t)
ondestd::begin
é importado para pesquisa de ADL et
é um Ivalue de tipoT
.Aqui está o meu código, juntamente com os comentários para explicar por que as coisas funcionam e os casos de teste que eu usei. Observe que isso requer C ++ 17 para compilar.
fonte
std::vector<T>
especialização e alterá-la para outro tipo de contêiner. A única coisa que você precisa é o caso 0 base para qualquer tipo que você não se especializam paraSupondo que um contêiner seja de qualquer tipo
value_type
eiterator
tipo de membro (os contêineres de biblioteca padrão atendem a esse requisito) ou uma matriz de estilo C, podemos generalizar facilmente a solução de Cruz Jean :Os tipos de contêiner podem ser ainda mais restritos, se necessário.
fonte
Você pode definir o seguinte modelo de classe
vector_depth<>
que corresponde a qualquer tipo:Esse modelo primário corresponde ao caso base que encerra a recursão. Em seguida, defina sua especialização correspondente para
std::vector<T>
:Essa especialização corresponde a
std::vector<T>
e corresponde ao caso recursivo.Por fim, defina o modelo de função,,
GetDepth()
que recorre ao modelo de classe acima:Exemplo:
A saída deste programa é:
fonte
std::vector
, mas por exemplo,GetDepth(v)
ondev
estáint
não será compilado. Seria melhor terGetDepth(const volatile T&)
e apenas voltarvector_depth<T>::value
.volatile
apenas permite que ele cubra mais coisas, sendo qualificado como cv máximo