Is it possible to declare two variables of different types in a for loop?

No - but technically there is a work-around (not that i'd actually use it unless forced to):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}

Not possible, but you can do:

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

Or, explicitly limit the scope of f and i using additional brackets:

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

C++17: Yes! You should use a structured binding declaration. The syntax has been supported in gcc and clang since gcc-7 and clang-4.0 (clang live example). This allows us to unpack a tuple like so:

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

The above will give you:

  • int i set to 1
  • double f set to 1.0
  • std::string s set to "ab"

Make sure to #include <tuple> for this kind of declaration.

You can specify the exact types inside the tuple by typing them all out as I have with the std::string, if you want to name a type. For example:

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

A specific application of this is iterating over a map, getting the key and value,

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

See a live example here


C++14: You can do the same as C++11 (below) with the addition of type-based std::get. So instead of std::get<0>(t) in the below example, you can have std::get<int>(t).


C++11: std::make_pair allows you to do this, as well as std::make_tuple for more than two objects.

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

std::make_pair will return the two arguments in a std::pair. The elements can be accessed with .first and .second.

For more than two objects, you'll need to use a 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) << '\n'; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tuple is a variadic template that will construct a tuple of any number of arguments (with some technical limitations of course). The elements can be accessed by index with std::get<INDEX>(tuple_object)

Within the for loop bodies you can easily alias the objects, though you still need to use .first or std::get for the for loop condition and update expression

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 << '\n'; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C++98 and C++03 You can explicitly name the types of a std::pair. There is no standard way to generalize this to more than two types though:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << '\n';
}

You can't declare multiple types in the initialization, but you can assign to multiple types E.G.

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

Just declare them in their own scope.


I think best approach is xian's answer.

but...


# Nested for loop

This approach is dirty, but can solve at all version.

so, I often use it in macro functions.

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
    }

Additional 1.

It can also be used to declare local variables and 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.
    }

Additional 2.

Good example : with macro function.

(If best approach can't be used because it is a for-loop-macro)

#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
    }

# If-statement trick

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

If you want initialize to 0 or nullptr, you can use this trick.

but I don't recommend this because of hard reading.

and it seems like bug.