É possível declarar duas variáveis ​​de tipos diferentes em um loop for?

240

É possível declarar duas variáveis ​​de tipos diferentes no corpo de inicialização de um loop for em C ++?

Por exemplo:

for(int i=0,j=0 ...

define dois números inteiros. Posso definir um inte um charno corpo de inicialização? Como isso seria feito?

Nathan Osman
fonte
3
É possível em g ++ - 4.4 ( -std=c++0x) na forma de for(auto i=0, j=0.0; ..., mas essa possibilidade foi removida em g ++ - 4.5 para coincidir com os textos c ++ 0x.
rafak

Respostas:

133

C ++ 17 : Sim! Você deve usar uma declaração de ligação estruturada . A sintaxe é suportada no gcc e no clang há anos (desde gcc-7 e clang-4.0) ( exemplo ao vivo do clang ). Isso nos permite descompactar uma tupla assim:

for (auto [i, f, s] = std::tuple{1, 1.0, std::string{"ab"}}; i < N; ++i, f += 1.5) {
    // ...
}

O exposto acima lhe dará:

  • int i definido como 1
  • double f definido como 1.0
  • std::string s definido como "ab"

Certifique-se de #include <tuple>que este tipo de declaração.

Você pode especificar os tipos exatos dentro do tuple, digitando todos eles como eu fiz com o std::string, se desejar nomear um tipo. Por exemplo:

auto [vec, i32] = std::tuple{std::vector<int>{3, 4, 5}, std::int32_t{12}}

Uma aplicação específica disso está iterando sobre um mapa, obtendo a chave e o valor,

std::unordered_map<K, V> m = { /*...*/ };
for (auto& [key, value] : m) {
   // ...
}

Veja um exemplo ao vivo aqui


C ++ 14 : você pode fazer o mesmo que o C ++ 11 (abaixo) com a adição de tipos std::get. Então, em vez de std::get<0>(t)no exemplo abaixo, você pode ter std::get<int>(t).


C ++ 11 : std::make_pairpermite fazer isso, além std::make_tuplede mais de dois objetos.

for (auto p = std::make_pair(5, std::string("Hello World")); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

std::make_pairretornará os dois argumentos em a std::pair. Os elementos podem ser acessados ​​com .firste .second.

Para mais de dois objetos, você precisará usar um std::tuple

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    std::cout << std::get<1>(t) << std::endl; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tupleé um modelo variável que construirá uma tupla de qualquer número de argumentos (com algumas limitações técnicas, é claro). Os elementos podem ser acessados ​​por índice comstd::get<INDEX>(tuple_object)

Dentro dos corpos do loop for, você pode facilmente alias os objetos, embora ainda precise usar .firstou std::getpara a condição do loop for e atualizar a expressão

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    auto& i = std::get<0>(t);
    auto& s = std::get<1>(t);
    auto& v = std::get<2>(t);
    std::cout << s << std::endl; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C ++ 98 e C ++ 03 Você pode nomear explicitamente os tipos de a std::pair. Porém, não há uma maneira padrão de generalizar isso para mais de dois tipos:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}
Ryan Haining
fonte
5
Se você estiver usando o C ++ 17, pode até largar o make_e escrever std::pair(1, 1.0).
Marc Glisse
O peludo C ++ 14 estilo de negócio tupla / pair - tudo de bom (provavelmente, upvoted), mas parece bizarro :)
mlvljr
3
Em resumo: Sim, é possível, mas não será bonito.
Algum programador
Sim, não é bonito, mas é uma droga! Absolutamente gostei do tuple-ish. :) Mas realmente é uma qualidade sintática muito intuitiva de loops em C ++ e me deu dor de cabeça por mais de meia hora para finalmente perceber o que tinha que ser Googled ...
aderchox
@aderchox se você pode esclarecer o mal-entendido que eu possa atualizar a resposta
Ryan Haining
276

Não - mas tecnicamente há uma solução alternativa (não que eu realmente a use, a menos que seja forçado a):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}
Georg Fritzsche
fonte
3
Isso não compilar em VS 2008, mas faz em Comeau on-line ;-)
JRL
7
@JRL: Ah, nem o VS2005. Ainda outro recurso de não conformidade no VC ++, eu acho.
Georg Fritzsche
3
Eu fiz o equivalente em Perl. Eu não tentei esconder algo assim através de uma revisão de código em C ++.
John
21
com c ++ 11 I você pode tornar este exemplo mais curto usando os valores padrãostruct { int a=0; char b='a'; } s;
Ryan Haining
1
Esta resposta cumpre os requisitos da resposta, mas a partir de um POV de legibilidade, eu prefiro o @MK. resposta de. A solução da MK até aborda o escopo adicionando chaves.
Trevor Boyd Smith
221

Não é possível, mas você pode fazer:

float f;
int i;
for (i = 0,f = 0.0; i < 5; i++)
{
  //...
}

Ou, limite explicitamente o escopo fe o iuso de colchetes adicionais:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}
MK.
fonte
Sei que essa é uma pergunta muito antiga, mas você pode explicar por que alguns fariam isso com os colchetes extras à sua volta, como no seu segundo exemplo?
Ford
13
@fizzisist para limitar explicitamente o escopo de f e i apenas a partes do código onde elas são usadas.
MK.
1
@MK. Obrigado, é o que eu suspeitava. Eu editei sua resposta para explicar isso.
ford
Apenas uma pergunta: por que assim? : O
rohan-patel
Porque funciona como 'int a = 0, b = 4', presumo. Dito isto, o escopo f e i provavelmente será útil apenas para evitar a reutilização desses nomes (o que é uma boa razão), mas o código gerado normalmente será o mesmo em um compilador moderno (neste caso).
Asu
14

Você não pode declarar vários tipos na inicialização, mas pode atribuir a vários tipos EG

{
   int i;
   char x;
   for(i = 0, x = 'p'; ...){
      ...
   }
}

Apenas declare-os em seu próprio escopo.

zmbush
fonte
3

Eu acho que a melhor abordagem é a resposta de xian .

mas...


# Aninhado para loop

Essa abordagem é suja, mas pode ser resolvida em todas as versões.

então, costumo usá-lo em funções macro.

for(int _int=0, /* make local variable */ \
    loopOnce=true; loopOnce==true; loopOnce=false)

    for(char _char=0; _char<3; _char++)
    {
        // do anything with
        // _int, _char
    }

Adicional 1.

Também pode ser usado para declare local variablese initialize global variables.

float globalFloat;

for(int localInt=0, /* decalre local variable */ \
    _=1;_;_=0)

    for(globalFloat=2.f; localInt<3; localInt++) /* initialize global variable */
    {
        // do.
    }

2 adicionais.

Bom exemplo: com função macro.

(Se a melhor abordagem não puder ser usada porque é uma macro de loop for)

#define for_two_decl(_decl_1, _decl_2, cond, incr) \
for(_decl_1, _=1;_;_=0)\
    for(_decl_2; (cond); (incr))


    for_two_decl(int i=0, char c=0, i<3, i++)
    {
        // your body with
        // i, c
    }

# Truque de declaração If

if (A* a=nullptr);
else
    for(...) // a is visible

Se você deseja inicializar para 0ou nullptr, pode usar este truque.

mas eu não recomendo isso por causa da leitura difícil.

e parece um bug.

mgcação
fonte
Nunca deixa de me surpreender como algumas pessoas pensam das outras. Eu nunca teria pensado em tais esquisitices. Idéias interessantes.
Dr. Person Person II
1

Consulte " Existe uma maneira de definir variáveis ​​de dois tipos no loop for? " Para outra maneira que envolve o aninhamento de múltiplos loops. A vantagem da outra maneira sobre o "truque de estrutura" de Georg é que (1) permite que você tenha uma mistura de variáveis ​​locais estáticas e não estáticas e (2) permite que você tenha variáveis ​​não copiáveis. A desvantagem é que é muito menos legível e pode ser menos eficiente.

tgoodhart
fonte
-2

Defina uma macro:

#define FOR( typeX,x,valueX,  typeY,y,valueY,  condition, increments) typeX x; typeY y; for(x=valueX,y=valueY;condition;increments)

FOR(int,i,0,  int,f,0.0,  i < 5, i++)
{
  //...
}

Lembre-se de que seus escopos variáveis ​​também não estarão dentro do loop for.

Ryan Favale
fonte
Você pode facilmente superar essa limitação envolvendo o código na macro em um escopo separado usando {e }.
Nathan Osman
4
Não, ele não podia. Sua macro não envolve o corpo do loop. Ele poderia adicionar um suporte de abertura extra, mas isso exigiria um suporte de fechamento "extra" ao usar a macro.
John
3
É uma ideia interessante, mas antes eu usaria qualquer uma das outras respostas antes de considerar isso.
Greg3
-2

Também você pode usar como abaixo em C ++.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
loyola
fonte