¿Es posible declarar dos variables de diferentes tipos en un bucle for?

240

¿Es posible declarar dos variables de diferentes tipos en el cuerpo de inicialización de un bucle for en C ++?

Por ejemplo:

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

define dos enteros. ¿Puedo definir una inty a charen el cuerpo de inicialización? ¿Cómo se haría esto?

Nathan Osman
fuente
3
Es posible en g ++ - 4.4 ( -std=c++0x) en forma de for(auto i=0, j=0.0; ..., pero esta posibilidad se eliminó en g ++ - 4.5 para coincidir con los textos de c ++ 0x.
rafak

Respuestas:

133

C ++ 17 : ¡Sí! Debe usar una declaración vinculante estructurada . La sintaxis ha sido compatible con gcc y clang durante años (desde gcc-7 y clang-4.0) ( ejemplo de clang live ). Esto nos permite descomprimir una tupla así:

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

Lo anterior te dará:

  • int i ajustado a 1
  • double f ajustado a 1.0
  • std::string s ajustado a "ab"

Asegúrese de que #include <tuple>para este tipo de declaración.

Puede especificar los tipos exactos dentro tuplede, escribiéndolos todos como tengo con el std::string, si desea nombrar un tipo. Por ejemplo:

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

Una aplicación específica de esto es iterar sobre un mapa, obtener la clave y el valor,

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

Vea un ejemplo en vivo aquí


C ++ 14 : puede hacer lo mismo que C ++ 11 (a continuación) con la adición de tipo basado std::get. Entonces, en lugar del std::get<0>(t)ejemplo a continuación, puede tener std::get<int>(t).


C ++ 11 : le std::make_pairpermite hacer esto, así como std::make_tuplepara más de dos 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_pairdevolverá los dos argumentos en a std::pair. Se puede acceder a los elementos con .firsty .second.

Para más de dos objetos, deberá usar un 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_tuplees una plantilla variada que construirá una tupla de cualquier número de argumentos (con algunas limitaciones técnicas, por supuesto). Se puede acceder a los elementos por índice constd::get<INDEX>(tuple_object)

Dentro de los cuerpos del bucle for puede alias fácilmente los objetos, aunque todavía necesita usar .firsto std::getpara la condición del bucle for y actualizar la expresión

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 y C ++ 03 Puede nombrar explícitamente los tipos de a std::pair. Sin embargo, no hay una forma estándar de generalizar esto a más de dos tipos:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}
Ryan Haining
fuente
55
Si está haciendo C ++ 17, incluso puede soltar make_y escribir std::pair(1, 1.0).
Marc Glisse
14 estilo de negocios tupla / pair El peluda C ++ - todo bien (probablemente, upvoted), pero parece extraño :)
mlvljr
3
En resumen: sí, es posible, pero no va a ser bonito.
Algún tipo programador el
Sí, no es bonito, ¡pero es tonto! Absolutamente disfruté del tuple-ish. :) Pero en realidad es una cualidad muy poco intuitivo sintáctica de los bucles en C ++ y me dio dolor de cabeza durante más de media hora para finalmente darse cuenta de lo que tenía que ser Googled ...
aderchox
@aderchox si puedes aclarar tu malentendido, puedo actualizar la respuesta
Ryan Haining
276

No, pero técnicamente hay una solución alternativa (no es que realmente lo use a menos que me obligue):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}
Georg Fritzsche
fuente
3
Esto no se compila en VS 2008, pero sí en Comeau en línea ;-)
JRL
77
@ JRL: Oh, tampoco VS2005. Otra característica de incumplimiento en VC ++, supongo.
Georg Fritzsche
3
He hecho el equivalente en Perl. Sin embargo, no he intentado esconder algo como esto a través de una revisión de código en C ++.
John
21
con c ++ 11 I puede acortar este ejemplo usando valores predeterminadosstruct { int a=0; char b='a'; } s;
Ryan Haining
1
Esta respuesta cumple los requisitos de la respuesta, pero desde un punto de vista de legibilidad prefiero @MK. La respuesta. La solución de MK incluso aborda el alcance agregando las llaves.
Trevor Boyd Smith
221

No es posible, pero puedes hacer:

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

O limite explícitamente el alcance fy el iuso de corchetes adicionales:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}
MK
fuente
Sé que esta es una pregunta muy antigua, pero ¿puede explicar por qué algunos lo harían con los corchetes adicionales a su alrededor, como en su segundo ejemplo?
Ford
13
@fizzisist para limitar explícitamente el alcance de fyi a solo partes del código donde se usan.
MK.
1
@MK. Gracias, eso es lo que sospechaba. Edité tu respuesta para explicar eso.
Ford
Solo una pregunta: ¿Por qué así? : O
rohan-patel
Porque funciona como 'int a = 0, b = 4', supongo. Dicho esto, el alcance de fyi probablemente sea útil solo para evitar la reutilización de esos nombres (lo cual es una razón justa), pero el código generado generalmente será el mismo en un compilador moderno (en este caso).
Asu
14

No puede declarar varios tipos en la inicialización, pero puede asignar a varios tipos EG

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

Simplemente declararlos en su propio alcance.

zmbush
fuente
3

Creo que el mejor enfoque es la respuesta de xian .

pero...


# Anidado para bucle

Este enfoque es sucio, pero puede resolverse en todas las versiones.

entonces, a menudo lo uso en funciones 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.

También se puede usar para declare local variablesy 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.
    }

Adicional 2.

Buen ejemplo: con función macro.

(Si el mejor enfoque no se puede usar porque es una macro para bucle 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
    }

# Truco de declaración if

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

Si desea inicializar a 0o nullptr, puede utilizar este truco.

pero no recomiendo esto debido a la lectura difícil.

y parece un error

mgcation
fuente
Nunca deja de sorprenderme lo diferentes que algunas personas piensan de otras. Nunca hubiera pensado en tales rarezas. Ideas interesantes
Dr. Person Person II
1

Consulte " ¿Hay alguna forma de definir variables de dos tipos en for loop? " Para conocer otra forma de anidar múltiples for loops. La ventaja de la otra manera sobre el "truco de estructura" de Georg es que (1) le permite tener una mezcla de variables locales estáticas y no estáticas y (2) le permite tener variables no copiables. La desventaja es que es mucho menos legible y puede ser menos eficiente.

tgoodhart
fuente
-2

Definir una 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++)
{
  //...
}

Solo recuerde, sus ámbitos variables tampoco estarán dentro del ciclo for de esta manera.

Ryan Favale
fuente
Puede superar fácilmente esa limitación envolviendo el código en la macro en un ámbito separado usando {y }.
Nathan Osman
44
No, no podía. Su macro no envuelve el cuerpo del bucle. Podría agregar un corchete de apertura adicional, pero eso requeriría un corchete de cierre "extra" cuando se usa la macro.
John
3
Es una idea interesante, pero preferiría usar cualquiera de las otras respuestas antes de considerar esto.
gregn3
-2

También podría usar como a continuación en C ++.

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