C++でforループを短く書く

#define REP(i, n) for(unsigned i = 0; i < (n); ++i)みたいなマクロはとても有名だ。N回のループをとても短く書くことができる。だが、これはあまりにもC++的ではない。nsignedな変数を渡した瞬間コンパイラが警告を出すし.....まあとにかくマクロを使わずに書けるところはマクロを使いたくない。マクロはバグの温床なんだ。

じゃあどうすれば短く書けるのか。そもそも私はどんな風にコードを書きたかったのかを考えると、やはりこういうのがC++的で美しいだろう。

for(auto i : repeat(3)) { }

こんなコードが書ければ万々歳。最高のC++ライフへまた一歩近づくことになる。

実装してみよう!

for(auto i : repeat(3))というのはrepeat tmp(3); for(auto i = tmp.begin(); i != tmp.end(); ++i)の糖衣構文みたいなものだ。実際にはどうなのか知らないが多分そんなものだ。 なのでイテレータっぽいものとbegin endを準備してやればこのコードは動いてくれる。

template<class T>
class repeat {
    static_assert(std::is_integral_v<T>);

    T first_;
    T last_;
public:
    constexpr repeat(T first, T last)
        : first_{ first }
        , last_{ last }
    {}
    constexpr explicit repeat(T count)
        : repeat{ 0, count }
    {}
    struct iterator {
        using difference_type = T;
        using value_type = T;
        using pointer = std::add_pointer_t<T>;
        using reference = std::add_lvalue_reference_t<T>;
        using iterator_category = std::input_iterator_tag;

        T val_;
        constexpr iterator(T val)
            : val_{ val }
        {}
        T operator*() const { return val_; }
        const T* operator->() const noexcept { return &val_; }
        T* operator->() noexcept { return &val_; }

        constexpr friend bool operator<(const iterator& a, const iterator& b) { return a.val_ < b.val_; }
        constexpr friend bool operator==(const iterator& a, const iterator& b) { return !(a < b) && !(b < a); }
        constexpr friend bool operator!=(const iterator& a, const iterator& b) { return !(a == b); }
        const iterator& operator++()
        {
            ++val_;
            return *this;
        }
        iterator operator++(int)
        {
            auto copy = *this;
            ++val_;
            return copy;
        }
    };
    
    constexpr iterator begin() const noexcept { return first_; };
    constexpr iterator end() const noexcept { return last_; }
};

できた。

圧倒的にREPマクロのほうが簡単でコードの量も少ないし多分高速だ。REPマクロを使おう。