// Qué llamadas hace realmente un for por rango: un iterador que avisa cada vez que lo usan.
#include <iostream>

namespace {

class TraceIterator {
public:
    explicit TraceIterator(int current) : current_(current) {}

    int operator*() const {
        std::cout << "  operator*\n";
        return current_;
    }
    TraceIterator& operator++() {
        std::cout << "  operator++\n";
        current_++;
        return *this;
    }
    bool operator!=(const TraceIterator& other) const {
        std::cout << "  operator!=\n";
        return current_ != other.current_;
    }

private:
    int current_;
};

class TraceRange {
public:
    explicit TraceRange(int count) : count_(count) {}

    TraceIterator begin() const {
        std::cout << "  begin\n";
        return TraceIterator(0);
    }
    TraceIterator end() const {
        std::cout << "  end\n";
        return TraceIterator(count_);
    }

private:
    int count_;
};

}  // namespace

int main() {
    std::cout << "for por rango:\n";
    for (int value : TraceRange(2)) {
        std::cout << "valor " << value << "\n";
    }

    std::cout << "ciclo equivalente:\n";
    TraceRange range(2);
    auto it = range.begin();
    auto last = range.end();
    for (; it != last; ++it) {
        int value = *it;
        std::cout << "valor " << value << "\n";
    }
    return 0;
}
