Span se refere a uma memória contínua, por exemplo, uma matriz;
template <typename T>
class Span {
public:
Span(T first, size_t s) ...;
Span(T first, T lasst) ...;
public:
// iterating underlying continuous memory;
begin() ...
end() ...
T & operator[](idx) ...
private:
T first;
T last;
};
A função de loop pode iterar todos os intervalos de passagem, como um loop de bolhas aninhado;
template <typename Func, typename ...Spans>
void loop(Func func, Spans &&...spans) {
// question: how to implements ?
}
exemplo
int main() {
int arr[] {1, 2, 3};
float arr2[] {1.1, 2.2};
int arr3[] {-1, -2, -3};
loop(func, Span(arr, 3), Span(arr2, 2));
/*
we can get:
func(1, 1.1);
func(1, 2.2);
func(2, 1.1);
func(2, 2.2);
func(3, 1.1);
func(3, 2.2);
*/
loop(func, Span(arr, 3), Span(arr2, 2), Span(arr3, 3));
/*
we can get:
func(1, 1.1, -1);
func(1, 1.1, -2);
func(1, 1.1, -3);
func(1, 2.2, -1);
func(1, 2.2, -2);
func(1, 2.2, -3);
func(2, 1.1, -1);
func(2, 1.1, -2);
...
*/
}
como implementar a função de loop, usando recursos da linguagem C++ até C++20 está ok, mas sem biblioteca padrão ou biblioteca de terceiros, apenas implementando manualmente;
nenhuma chamada de função de recursão, melhor for loop; faz com que a recursão facilmente cause estouro de pilha;
usando vetor e tupla, a ligação de estrutura está ok;
Com C++23 isso é muito mais fácil graças a
std::views::cartesian_product()
:parafuso divino
Se você estiver limitado ao C++20, terá que fazer o produto cartesiano sozinho - por exemplo, usando recursão e vinculando o valor atual de cada intervalo à função:
parafuso divino
Nenhuma implementação de biblioteca padrão: (conforme solicitado nos comentários)
godbolt
Você pode combinar
ranges::for_each
com C++23views::cartesian_product
como:Aqui está uma simplificação de uma resposta deste post que permite gerar em tempo de compilação o produto cartesiano de várias matrizes.
Demonstração
A ideia principal é iterar um inteiro
i
de 0 a N-1 (com N o número de entradas possíveis do produto cartesiano) e desse inteiro 'global', recupera-se como uma tupla o índiceidx
de cada array que faz a entrada atual. Então, é preciso apenas chamar a função fornecidafct
com argumentos sendo uma expressão fold nesse índiceidx
.Ele usa principalmente expressões fold e
std::make_index_sequence
/std::index_sequence
. Ele também supõe que os arrays fornecidos suportam métodosoperator[]
esize
.Funciona com c++17 e superiores.
Aqui está uma alternativa que usa um array para manter o controle dos índices para cada um dos spans. O array de índices é declarado para ter
nSpans+1
elementos onde o primeiro elemento serve como um sentinela. O arrayspanSizes
é declarado para ter o mesmo tamanho e também ter um sentinela. A variávelcarryIndex
mantém o controle de qual índice deve ser incrementado. A palavra "carry" aqui se refere ao fato de que os índices são incrementados de uma maneira similar a carries em adições.Demonstração: https://godbolt.org/z/ejzhe7e5G (
main()
Foi retirado da demonstração na resposta de @Turtlefight )