No C ++ 11, como escrever uma função (ou método) que usa um std :: array de tipo conhecido, mas tamanho desconhecido?
// made up example
void mulArray(std::array<int, ?>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6> arr2;
std::array<int, 95> arr3;
mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);
Durante minha pesquisa, encontrei apenas sugestões para usar modelos, mas essas parecem confusas (definições de método no cabeçalho) e excessivas para o que estou tentando realizar.
Existe uma maneira simples de fazer isso funcionar, como faria com arrays simples no estilo C?
std::vector
.std::vector
como recomenda @TravisPessetto?Respostas:
Não. Você realmente não pode fazer isso a menos que torne sua função um modelo de função (ou use outro tipo de contêiner, como um
std::vector
, conforme sugerido nos comentários à pergunta):Aqui está um exemplo ao vivo .
fonte
template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
O tamanho de
array
faz parte do tipo , então você não pode fazer exatamente o que deseja. Existem algumas alternativas.O preferido seria usar um par de iteradores:
Como alternativa, use em
vector
vez de array, que permite armazenar o tamanho no tempo de execução, em vez de como parte de seu tipo:fonte
Eu tentei abaixo e funcionou para mim.
RESULTADO :
1 2 3 4 5 6 7
2 4 6 8 10 12
1 1 1 1 1 1 1 1 1
3 6 9 12 15 18 21
10 20 30 40 50 60
2 2 2 2 2 2 2 2 2
fonte
template
.auto foo(auto bar) { return bar * 2; }
não é C ++ válido no momento, embora seja compilado em GCC7 com o sinalizador C ++ 17 definido. Da leitura aqui , os parâmetros da função declarados como auto fazem parte do TS de conceitos que deve eventualmente fazer parte do C ++ 20.EDITAR
C ++ 20 inclui provisoriamente
std::span
https://en.cppreference.com/w/cpp/container/span
Resposta Original
O que você quer é algo como
gsl::span
, que está disponível na Biblioteca de Suporte de Diretrizes descrita nas Diretrizes Básicas de C ++:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views
Você pode encontrar uma implementação somente de cabeçalho de código aberto da GSL aqui:
https://github.com/Microsoft/GSL
Com
gsl::span
, você pode fazer isso:O problema com
std::array
é que seu tamanho é parte de seu tipo, então você teria que usar um modelo para implementar uma funçãostd::array
de tamanho arbitrário.gsl::span
por outro lado, armazena seu tamanho como informação de tempo de execução. Isso permite que você use uma função não modelo para aceitar uma matriz de tamanho arbitrário. Também aceitará outros contêineres contíguos:Muito legal, hein?
fonte
Com certeza, existe uma maneira simples no C ++ 11 de escrever uma função que recebe um std :: array de tipo conhecido, mas de tamanho desconhecido.
Se não formos capazes de passar o tamanho do array para a função, então, em vez disso, podemos passar o endereço de memória de onde o array começa junto com um segundo endereço de onde o array termina. Posteriormente, dentro da função, podemos usar esses 2 endereços de memória para calcular o tamanho do array!
Saída no console: 10, 20, 2, 4, 8
fonte
Isso pode ser feito, mas são necessárias algumas etapas para fazer de forma limpa. Primeiro, escreva um
template class
que represente um intervalo de valores contíguos. Em seguida, encaminhe umatemplate
versão que saiba o quão grandearray
é para aImpl
versão que leva esse intervalo contíguo.Finalmente, implemente a
contig_range
versão. Observe quefor( int& x: range )
funciona paracontig_range
, porque implementeibegin()
eend()
e os ponteiros são iteradores.(não testado, mas o design deve funcionar).
Então, em seu
.cpp
arquivo:Isso tem a desvantagem de que o código que faz um loop sobre o conteúdo do array não sabe (em tempo de compilação) o tamanho do array, o que pode custar a otimização. Tem a vantagem de que a implementação não precisa estar no cabeçalho.
Tenha cuidado ao construir explicitamente um
contig_range
, pois se você passá-lo um,set
ele assumirá que osset
dados são contíguos, o que é falso, e terá um comportamento indefinido em todo o lugar. Os únicos doisstd
contêineres em que isso funciona sãovector
earray
(e arrays de estilo C, por acaso!).deque
apesar de ser aleatório, o acesso não é contíguo (perigosamente, é contíguo em pequenos pedaços!),list
não é nem perto, e os containers associativos (ordenados e não ordenados) são igualmente não contíguos.Assim, os três construtores I implementadas quando
std::array
,std::vector
e C-estilo matrizes, que abrange basicamente as bases.Implementação
[]
é tão fácil assim, e entrefor()
e[]
que é mais do que você quer umarray
para, não é?fonte
template
função realmente curta com quase nenhum detalhe de implementação. AImpl
função não é umatemplate
função e, portanto, você pode ocultar a implementação no.cpp
arquivo de sua escolha. É um tipo realmente grosseiro de eliminação de tipo, em que extraio a capacidade de iterar em contêineres contíguos para uma classe mais simples e, em seguida, transmito isso ... (emboramultArrayImpl
tome atemplate
como argumento, não é atemplate
si mesmo).&*
dereferencia o iterador (que pode não ser um ponteiro) e, em seguida, faz um ponteiro para o endereço. Para dados de memória contígua, o ponteiro parabegin
e o ponteiro para um-passado-oend
também são iteradores de acesso aleatório e são do mesmo tipo para cada intervalo contíguo sobre um tipoT
.