Como posso iterar em uma tupla (usando C ++ 11)? Tentei o seguinte:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
mas isso não funciona:
Erro 1: desculpe, não implementado: não é possível expandir 'Listener ...' em uma lista de argumentos de comprimento fixo.
Erro 2: não posso aparecer em uma expressão constante.
Então, como faço para iterar corretamente os elementos de uma tupla?
Respostas:
Boost.Fusion é uma possibilidade:
Exemplo não testado:
fonte
Eu tenho uma resposta baseada na iteração sobre uma tupla :
A idéia usual é usar recursão em tempo de compilação. Na verdade, essa ideia é usada para tornar um printf seguro para tipos, conforme observado nos documentos de tupla originais.
Isso pode ser facilmente generalizado em
for_each
tuplas for:Embora isso requeira algum esforço para
FuncT
representar algo com as sobrecargas apropriadas para cada tipo que a tupla possa conter. Isso funciona melhor se você souber que todos os elementos da tupla compartilharão uma classe base comum ou algo semelhante.fonte
enable_if
documentação .for_each
. Na verdade, eu mesmo fiz. :-) Acho que esta resposta seria mais útil se já fosse generalizada.const std::tuple<Tp...>&
.. Se você não pretende modificar tuplas durante a iteração, essasconst
versões serão suficientes.Em C ++ 17, você pode usar
std::apply
com expressão de dobra :Um exemplo completo para imprimir uma tupla:
[Exemplo online no Coliru]
Esta solução resolve o problema da ordem de avaliação na resposta de M. Alaggan .
fonte
((std::cout << args << '\n'), ...);
:? O lambda é invocado uma vez com os elementos da tupla descompactados comoargs
, mas o que há com os parênteses duplos?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
aqui.Em C ++ 17, você pode fazer isso:
Isso já funciona no Clang ++ 3.9, usando std :: experimental :: apply.
fonte
do_something()
- ocorrendo em uma ordem não especificada, porque o pacote de parâmetros é expandido dentro de uma chamada de função()
, em que os argumentos têm uma ordem não especificada? Isso pode ser muito significativo; Eu imagino que a maioria das pessoas esperaria que a ordenação ocorresse na mesma ordem que os membros, ou seja, como os índicesstd::get<>()
. AFAIK, para ter pedido garantido em casos como este, a expansão deve ser feita dentro{braces}
. Estou errado? Esta resposta enfatiza essa ordem: stackoverflow.com/a/16387374/2757035Use Boost.Hana e lambdas genéricos:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
fonte
using namespace boost::fusion
(especialmente junto comusing namespace std
). Agora não há como saber sefor_each
éstd::for_each
ouboost::fusion::for_each
C ++ está introduzindo instruções de expansão para esse propósito. Eles estavam originalmente no caminho certo para o C ++ 20, mas por pouco perderam o corte devido à falta de tempo para revisão da redação do idioma (veja aqui e aqui ).
A sintaxe atualmente aceita (veja os links acima) é:
fonte
Uma maneira mais simples, intuitiva e amigável do compilador de fazer isso em C ++ 17, usando
if constexpr
:Esta é a recursão em tempo de compilação, semelhante à apresentada por @emsr. Mas isso não usa SFINAE, então (eu acho) é mais amigável ao compilador.
fonte
Você precisa usar a metaprogramação de modelo, mostrada aqui com Boost.Tuple:
Em C ++ 0x, você pode escrever
print_tuple()
como uma função de modelo variável.fonte
Primeiro defina alguns auxiliares de índice:
Com sua função, você gostaria de aplicar em cada elemento da tupla:
você pode escrever:
Ou se
foo
retornarvoid
, useNota: Em C ++ 14
make_index_sequence
já está definido ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Se você precisar de um pedido de avaliação da esquerda para a direita, considere algo assim:
fonte
foo
paravoid
antes de invocaroperator,
para evitar uma possível sobrecarga do operador patológico.Esta é uma maneira fácil em C ++ 17 de iterar itens de tupla apenas com a biblioteca padrão:
Exemplo:
Resultado:
Isso pode ser estendido para quebrar condicionalmente o loop no caso de o chamável retornar um valor (mas ainda funcionar com os chamáveis que não retornam um valor atribuível bool, por exemplo, void):
Exemplo:
Resultado:
fonte
Se você quiser usar std :: tuple e tiver um compilador C ++ que suporte modelos variadic, tente o código abaixo (testado com g ++ 4.5). Esta deve ser a resposta à sua pergunta.
boost :: fusion é outra opção, mas requer seu próprio tipo de tupla: boost :: fusion :: tuple. É melhor seguir o padrão! Aqui está um teste:
o poder dos modelos variadic!
fonte
No MSVC STL, há uma função _For_each_tuple_element (não documentada):
fonte
Outros mencionaram algumas bibliotecas de terceiros bem projetadas às quais você pode recorrer. No entanto, se você estiver usando C ++ sem essas bibliotecas de terceiros, o código a seguir pode ajudar.
Observação: o código é compilado com qualquer compilador compatível com C ++ 11 e mantém a consistência com o design da biblioteca padrão:
A tupla não precisa ser
std::tuple
e, em vez disso, pode ser qualquer coisa que suportestd::get
estd::tuple_size
; em particular,std::array
estd::pair
pode ser usado;A tupla pode ser um tipo de referência ou qualificada pelo CV;
Ele tem um comportamento semelhante ao
std::for_each
e retorna a entradaUnaryFunction
;Para usuários de C ++ 14 (ou versão mais recente),
typename std::enable_if<T>::type
etypename std::decay<T>::type
pode ser substituído por sua versão simplificadastd::enable_if_t<T>
estd::decay_t<T>
;Para usuários de C ++ 17 (ou versão mais recente),
std::tuple_size<T>::value
pode ser substituído por sua versão simplificadastd::tuple_size_v<T>
,.Para usuários do C ++ 20 (ou versão mais recente), o
SFINAE
recurso pode ser implementado com oConcepts
.fonte
Usar
constexpr
eif constexpr
(C ++ 17) é bastante simples e direto:fonte
Posso ter perdido este trem, mas ele estará aqui para referência futura.
Aqui está minha construção com base nesta resposta e nesta essência :
Você então o usa da seguinte maneira:
Pode haver espaço para melhorias.
De acordo com o código do OP, seria este:
fonte
De todas as respostas que vi aqui, aqui e aqui , gostei mais da maneira de iterar do @sigidagi . Infelizmente, sua resposta é muito prolixa, o que, em minha opinião, obscurece a clareza inerente.
Esta é a minha versão de sua solução que é mais conciso e trabalha com
std::tuple
,std::pair
estd::array
.Demo: coliru
C ++ 14's
std::make_index_sequence
podem ser implementados para C ++ 11 .fonte
A tupla de boost fornece funções auxiliares
get_head()
e,get_tail()
portanto, suas funções auxiliares podem ter a seguinte aparência:conforme descrito aqui http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
com
std::tuple
ele deve ser semelhante.Na verdade, infelizmente
std::tuple
não parece fornecer essa interface, então os métodos sugeridos antes devem funcionar, ou você precisaria mudar para osboost::tuple
quais tem outros benefícios (como operadores io já fornecidos). Embora haja uma desvantagemboost::tuple
com o gcc - ele ainda não aceita modelos variados, mas isso pode já estar corrigido, pois não tenho a última versão do boost instalado em minha máquina.fonte
Eu tropecei no mesmo problema para iterar sobre uma tupla de objetos de função, então aqui está mais uma solução:
Resultado:
fonte
Outra opção seria implementar iteradores para tuplas. Isso tem a vantagem de poder usar uma variedade de algoritmos fornecidos pela biblioteca padrão e loops for baseados em intervalo. Uma abordagem elegante para isso é explicada aqui https://foonathan.net/2017/03/tuple-iterator/ . A ideia básica é transformar tuplas em um intervalo com métodos
begin()
eend()
para fornecer iteradores. O próprio iterador retorna umstd::variant<...>
que pode então ser visitado usandostd::visit
.Aqui estão alguns exemplos:
Minha implementação (que é fortemente baseada nas explicações do link acima):
O acesso somente leitura também é suportado passando um
const std::tuple<>&
parato_range()
.fonte
Expandindo a resposta @Stypox, podemos tornar sua solução mais genérica (C ++ 17 em diante). Adicionando um argumento de função chamável:
Então, precisamos de uma estratégia para visitar cada tipo.
Vamos começar com alguns auxiliares (os dois primeiros retirados de cppreference):
variant_ref
é usado para permitir que o estado das tuplas seja modificado.Uso:
Resultado:
Para completar, aqui estão meus
Bar
&Foo
:fonte