333

Is it possible to declare two variables of different types in the initialization body of a for loop in C++?

For example:

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

defines two integers. Can I define an int and a char in the initialization body? How would this be done?

3
  • 6
    It is possible in g++-4.4 (-std=c++0x) in the form of for(auto i=0, j=0.0; ..., but this possibility was removed in g++-4.5 to coincide with the c++0x texts. Commented May 9, 2010 at 11:11
  • 1
    Since this question pops up first for many who intent to look for the same question in C, here is the C equivalent. Commented Jul 25, 2020 at 17:10
  • Note for myself: Read stackoverflow.com/a/2687427/5290519. Commented Feb 20, 2021 at 9:51

9 Answers 9

322

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; } 
Sign up to request clarification or add additional context in comments.

3 Comments

with c++11 I you can make this example shorter using default values struct { int a=0; char b='a'; } s;
@TrevorBoydSmith: That's so ugly, variables scatters around.
Thanks for this. I just sat here maniacally laughing to myself over: for(struct { std::vector<float>::iterator it; size_t count; } v { vec.begin(), 1 }; v.it < vec.end(); ++v.it, ++v.count) { ... }
256

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'; } 

5 Comments

If you are doing C++17, you can even drop the make_ and write std::pair(1, 1.0).
The hairy C++14 style tuple / pair business -- all good (probably, upvoted), but looks bizarre :)
In short:Yes it's possible, but not going to be pretty.
I understand this is "idiomatic" C++ (at the time around C++17, at least), but by the gods that's really ugly. The whole std namespace living in pretension it's part of the language, is just one giant step in the wrong direction, leaped too far, in my opinion.
At this point, I'd forget about the whole declaring-multiple-variables-of-different-types-in-a-for-loop-initialization-thing and just declare them before the loop.
251

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

7 Comments

I know this is a very old question, but can you explain why some would do it with the extra brackets around it, as in your second example?
@fizzisist to explicitly limit the scope of f and i to only parts of the code where they are used.
@MK. Thanks, that's what I suspected. I edited your answer to explain that.
Only one question: Why like this? :O
Because it works like 'int a = 0, b = 4', I assume. That being said, scoping f and i will likely be useful only to prevent reusing those names (which is a fair reason), but the generated code will typically be the same on a modern compiler (in this case).
|
15

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.

1 Comment

But this will make your indentation ugly as hell.
5

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.

4 Comments

Note that "break" and "continue" will not work as expected here.
@Michaël: Why? Wouldn't one continue the inner-most for in all of the examples?
There's also: for(int i = 0; i < whatever; i++) if (A & a = get_a(i)) that does not break break and continue and is transparent. The downside is that A must implement an explicit operator bool returning true.
excellent if statement trick, thanks
0

See "Is there a way to define variables of two types in for loop?" for another way involving nesting multiple for loops. The advantage of the other way over Georg's "struct trick" is that it (1) allows you to have a mixture of static and non-static local variables and (2) it allows you to have non-copyable variables. The downside is that it is far less readable and may be less efficient.

Comments

0

just like this,Is it ok?? wrap your for loop with {}

{ int i=0; char *str="abc"; for(;i<9&&*str=='a';i++,str++) { //do something } } 

Comments

-2

Also you could use like below in C++.

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

Comments

-5

Define a 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++) { //... } 

Just remember, your variable scopes will not be within the for loop this way either.

3 Comments

You could easily overcome that limitation by wrapping the code in the macro in a separate scope using { and }.
No he couldn't. His macro doesn't wrap the loop body. He could add an extra openning bracket, but that would require an "extra" closing bracket when using the macro.
It's an interesting idea, but I would sooner use any of the other answers before considering this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.