Skip to content

Auto_range_based

Modern C++ (C++11 and later) introduced features that make code cleaner and less error-prone. This chapter covers auto and range-based for loops.

auto automatically deduces the type of a variable from its initializer:

// Before C++11
std::vector<int>::iterator it = vec.begin();
// With auto
auto it = vec.begin(); // Type deduced as std::vector<int>::iterator
auto x = 42; // int
auto y = 3.14; // double
auto z = "hello"; // const char*
auto flag = true; // bool
std::vector<int> vec = {1, 2, 3};
auto& ref = vec[0]; // int& (reference)
auto&& rref = vec[0]; // int& (lvalue ref, not rvalue ref)
auto* ptr = &vec[0]; // int*
const int x = 42;
auto y = x; // int (const is dropped)
auto& z = x; // const int& (const preserved)
const auto w = x; // const int
// Return type deduction (C++14)
auto add(int a, int b) {
return a + b; // Returns int
}
// Trailing return type
auto multiply(int a, int b) -> int {
return a * b;
}

The range-based for loop provides a cleaner way to iterate over containers:

std::vector<int> vec = {1, 2, 3, 4, 5};
// Traditional
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// Range-based (C++11)
for (int val : vec) {
std::cout << val << " ";
}
std::vector<int> vec = {1, 2, 3, 4, 5};
// Copy (original unchanged)
for (auto val : vec) {
val *= 2; // Modifies copy only
}
// Reference (original modified)
for (auto& val : vec) {
val *= 2;
}
// Const reference (read-only, no copy)
for (const auto& val : vec) {
std::cout << val << " ";
}
// C++20: with initialization
for (std::vector<int> v = {1, 2, 3}; auto& val : v) {
// Use val
}
int arr[] = {1, 2, 3, 4, 5};
for (int val : arr) {
std::cout << val << " ";
}
// String
std::string str = "hello";
for (char c : str) {
std::cout << c << " ";
}
for (int x : {1, 2, 3, 4, 5}) {
std::cout << x << " ";
}

Extract multiple values at once:

#include <tuple>
#include <map>
// Pair
auto [key, value] = std::make_pair("hello", 42);
// Tuple
auto [a, b, c] = std::make_tuple(1, 2.5, "three");
// Map iteration
std::map<std::string, int> ages = {{"Alice", 30}};
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << "\n";
}
// Struct
struct Point { int x, y; };
Point p{1, 2};
auto [px, py] = p;
#include <iostream>
#include <vector>
#include <map>
#include <string>
int main() {
// auto with containers
std::vector<std::pair<std::string, int>> items = {
{"apple", 5},
{"banana", 3},
{"cherry", 10}
};
// Range-based for with structured bindings
for (const auto& [fruit, count] : items) {
std::cout << fruit << ": " << count << "\n";
}
// auto with STL algorithms
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
auto sorted = numbers; // Copy
std::sort(sorted.begin(), sorted.end());
for (const auto& n : sorted) {
std::cout << n << " ";
}
std::cout << "\n";
// Find with auto
auto it = std::find(numbers.begin(), numbers.end(), 8);
if (it != numbers.end()) {
std::cout << "Found: " << *it << "\n";
}
return 0;
}
  1. Use auto for type aliases when type is obvious
auto it = vec.begin(); // Clear
auto result = calculate(); // Less clear
  1. Use const auto& for read-only iteration
for (const auto& item : container) { }
  1. Use auto for long type names
std::vector<std::map<std::string, std::vector<int>>>::iterator it;
auto it = ...; // Much cleaner
  • auto deduces type from initializer
  • Use auto& for references, const auto& for read-only
  • Range-based for loops simplify container iteration
  • Structured bindings (C++17) unpack tuples/pairs/structs
  • Modern C++ reduces verbosity while maintaining type safety

Let’s learn about lambda expressions - a powerful feature for creating anonymous function objects.

Next Chapter: 25_lambda_expressions.md - Lambda Expressions