SyntaxStudy
Sign Up
C++ C++11 Essentials: auto, Range-for, and Initialiser Lists
C++ Beginner 1 min read

C++11 Essentials: auto, Range-for, and Initialiser Lists

C++11 brought a wave of language improvements that dramatically improved expressiveness and safety. The 'auto' keyword instructs the compiler to deduce the type of a variable from its initialiser. This reduces verbosity — especially with complex template types — and makes code more resilient to type changes. However, 'auto' should not be used blindly: when the type is important for documentation purposes, naming it explicitly is preferable. Range-based for loops provide clean syntax for iterating over any range — containers, arrays, initialiser lists, or any type that provides 'begin()' and 'end()' — without exposing iterator boilerplate. Combining range-for with 'const auto&' avoids unnecessary copies of elements, while using 'auto&' allows in-place modification. Brace initialisation with initialiser lists provides a uniform syntax for initialising any object, from scalars to aggregates to STL containers. It also prevents narrowing conversions — assigning a 'double' to an 'int' using braces is a compile error, unlike assignment with parentheses or '='. Together, 'auto', range-for, and brace-init form the core of idiomatic modern C++.
Example
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <initializer_list>

// Function accepting initialiser list
double average(std::initializer_list<double> values) {
    double sum = 0;
    for (double v : values) sum += v;
    return sum / values.size();
}

class Config {
public:
    // Initialiser-list constructor
    Config(std::initializer_list<std::pair<std::string,std::string>> pairs) {
        for (const auto& [key, val] : pairs)
            data_[key] = val;
    }
    std::string get(const std::string& key) const {
        auto it = data_.find(key);
        return it != data_.end() ? it->second : "";
    }
private:
    std::map<std::string, std::string> data_;
};

int main() {
    // auto: type deduced from initialiser
    auto x = 42;
    auto pi = 3.14159;
    auto msg = std::string("hello");

    // Brace-init: no narrowing conversions allowed
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};

    // Range-for with const auto& (no copy)
    std::cout << "Scores:\n";
    for (const auto& [name, score] : scores)
        std::cout << "  " << name << ": " << score << "\n";

    // Initialiser list in function call
    std::cout << "avg = " << average({1.0, 2.0, 3.0, 4.0, 5.0}) << "\n";

    // Config with initialiser-list constructor
    Config cfg = {{"host", "localhost"}, {"port", "8080"}};
    std::cout << "host=" << cfg.get("host")
              << " port=" << cfg.get("port") << "\n";

    return 0;
}