For Loop
Programming
The for loop is one of the most fundamental control structures in C++. While it may seem basic, there are many variations and subtle details that can significantly impact code quality, performance, and readability. In C++, there are numerous ways to write a for loop, each suited to different scenarios.
Traditional For Loop
The classic for loop with initialization, condition, and increment:
#include <iostream>
int main() {
// Basic counting loop
for (int i = 1; i <= 5; ++i) {
std::cout << "Number: " << i << std::endl;
}
return 0;
} Loop Structure
The for loop has three parts:
- Initialization:
int i = 1- executed once before the loop starts - Condition:
i <= 5- checked before each iteration, loop continues while true - Increment:
++i- executed after each iteration
Counting Down
#include <iostream>
int main() {
// Countdown loop
for (int i = 10; i >= 1; --i) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
} Custom Step Size
#include <iostream>
int main() {
// Loop with step size of 2
for (int i = 0; i < 10; i += 2) {
std::cout << i << " ";
}
std::cout << std::endl;
// Loop with step size of 5
for (int i = 0; i <= 20; i += 5) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
} Range-Based For Loop (C++11)
Modern C++ provides range-based for loops for iterating over containers:
Basic Range-Based Loop
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Copy each element (less efficient)
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
} Range-Based Loop with Reference
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Modify elements in place
for (int& num : numbers) {
num *= 2; // Double each element
}
// Print modified vector
for (const int& num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
} Range-Based Loop with Const Reference
#include <iostream>
#include <vector>
#include <string>
int main() {
std::vector<std::string> words = {"hello", "world", "c++"};
// Read-only access (most efficient for large objects)
for (const std::string& word : words) {
std::cout << word << " ";
}
std::cout << std::endl;
return 0;
} Range-Based Loop with Auto
#include <iostream>
#include <vector>
#include <map>
int main() {
std::vector<int> vec = {1, 2, 3};
// Auto deduces type automatically
for (auto& elem : vec) {
elem += 10;
}
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
// Auto with structured bindings (C++17)
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << std::endl;
}
return 0;
} Iterator-Based For Loop
Using explicit iterators for more control:
#include <iostream>
#include <vector>
#include <list>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// Traditional iterator syntax
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// With auto (cleaner)
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// Const iterator (read-only)
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
} Control Flow in For Loops
Break Statement
#include <iostream>
int main() {
// Exit loop early when condition is met
for (int i = 0; i < 10; ++i) {
if (i == 5) {
break; // Exit the loop immediately
}
std::cout << i << " ";
}
std::cout << std::endl;
// Output: 0 1 2 3 4
return 0;
} Continue Statement
#include <iostream>
int main() {
// Skip current iteration and continue
for (int i = 0; i < 10; ++i) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
std::cout << i << " ";
}
std::cout << std::endl;
// Output: 1 3 5 7 9
return 0;
} Nested For Loops
#include <iostream>
int main() {
// 2D iteration
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
std::cout << "(" << i << ", " << j << ") ";
}
std::cout << std::endl;
}
// Multiplication table
for (int i = 1; i <= 5; ++i) {
for (int j = 1; j <= 5; ++j) {
std::cout << i * j << " ";
}
std::cout << std::endl;
}
return 0;
} Infinite For Loop
#include <iostream>
int main() {
// Infinite loop (must have break condition)
int count = 0;
for (;;) {
std::cout << count << " ";
count++;
if (count >= 5) {
break; // Exit condition
}
}
std::cout << std::endl;
return 0;
} For Loop with Standard Algorithms
std::for_each with Lambda
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// Lambda function
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
// Lambda with capture
int sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int n) {
sum += n;
});
std::cout << "Sum: " << sum << std::endl;
return 0;
} Best Practices
- Prefer range-based for loops: More readable and less error-prone for containers
- Use const references:
for (const auto& elem : container)for read-only access to avoid copies - Use references when modifying:
for (auto& elem : container)to modify elements - Prefer
++ioveri++: Slightly more efficient (no temporary object) - Use meaningful variable names:
i, j, kare fine for simple loops, but use descriptive names when appropriate - Avoid modifying container during iteration: Can invalidate iterators
- Use
size_tfor size comparisons: Avoids signed/unsigned comparison warnings
Common Pitfalls
- Off-by-one errors: Using
<=instead of<or vice versa - Modifying container during iteration: Can cause undefined behavior
- Signed/unsigned comparison: Comparing
intwithsize()can cause warnings - Infinite loops: Forgetting break conditions in infinite loops
- Copying large objects: Using value instead of reference in range-based loops
- Iterator invalidation: Modifying containers while iterating with iterators
Performance Considerations
- Range-based for loops are optimized by the compiler and often as fast as iterator-based loops
- Using references avoids unnecessary copies, especially important for large objects
++iis slightly faster thani++for non-primitive types (no temporary object)- Prefer
constiterators when you don't need to modify elements
When to Use Each Type
- Traditional for loop: When you need explicit control over the index, custom step sizes, or counting
- Range-based for loop: Preferred for iterating over containers when you don't need the index
- Iterator-based loop: When you need to modify the container structure or use iterator-specific operations
- std::for_each: When you want to apply a function to each element (functional programming style)