C++11 实现强大的 Range(...)

699 阅读1分钟

「这是我参与11月更文挑战的第 8 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 17 篇文章

C++11 增加了使用 : 来实现基于 range 的 for 循环新特性,这种循环方式非常简洁直白,它的内部实现是对传统的迭代器 begin()/end() 的遍历做了封装。使用实例代码如下:

/// @note 遍历 vector
std::vector<int> v;
for (auto i : v)
{
    cout << i << endl;
}

/// @note 遍历 map
std::map<string, int> map;
for (const auto &item : map) ///< 只读方式
{
    cout << item->first << item->second << endl;
}

C++11 基于 range 的 for 循环强大的地方在于它可以支持自定义类型的遍历,但是自定义类型必须满足以下三个条件:

  • 要实现 begin()(返回第一个元素的迭代器) 和 end() (返回最后一个元素的迭代器);
  • 提供迭代终止的方法;
  • 提供遍历 range 的方法;

虽然 C++11 的标准库中有容器如 vectorlistqueuemap、初始化列表和 array 等等都已经支持了基于 range 的 for 循环,但是它们还不够强大,我们的目的,是想构造一个可以根据步长(支持浮点数、字符、正数为前向迭代、负数为反向迭代)生成有序序列的 Range 。

具体实现代码如下:

namespace Cosmos
{
    template <typename value_t>
    class RangeImpl
    {
        class Iterator;

    public:
        RangeImpl(value_t begin, value_t end, value_t step = 1) : m_begin(begin), m_end(end), m_step(step)
        {
            if (step > 0 && m_begin >= m_end)
                throw std::logic_error("end must greater than begin.");
            else if (step < 0 && m_begin <= m_end)
                throw std::logic_error("end must less than begin.");

            m_step_end = (m_end - m_begin) / m_step; ///< 步数
            if (m_begin + m_step_end * m_step != m_end)
            {
                m_step_end++; ///< 补一步
            }
        }

        Iterator begin()
        {
            return Iterator(0, *this); ///< 返回首元素迭代器
        }

        Iterator end()
        {
            return Iterator(m_step_end, *this); ///< 返回末尾元素迭代器
        }

        value_t operator[](int s) ///< 返回指定索引的值
        {
            return m_begin + s * m_step;
        }

        int size()
        {
            return m_step_end;
        }

    private:
        value_t m_begin;
        value_t m_end;
        value_t m_step;
        int m_step_end;

        class Iterator
        {
        public:
            Iterator(int start, RangeImpl &range) : m_current_step(start), m_range(range)
            {
                m_current_value = m_range.m_begin + m_current_step * m_range.m_step;
            }

            value_t operator*() { return m_current_value; }

            const Iterator *operator++() ///< 正向遍历
            {
                m_current_value += m_range.m_step;
                m_current_step++;
                return this;
            }

            bool operator==(const Iterator &other) ///< 步数是否相同
            {
                return m_current_step == other.m_current_step;
            }

            bool operator!=(const Iterator &other) ///< 步数是否不同
            {
                return m_current_step != other.m_current_step;
            }

            const Iterator *operator--() ///< 反向遍历
            {
                m_current_value -= m_range.m_step;
                m_current_step--;
                return this;
            }

        private:
            value_t m_current_value; ///< 当前值
            int m_current_step;      ///< 当前步数
            RangeImpl &m_range;
        };
    };

    /// @note 三个参数
    template <typename T, typename V>
    auto Range(T begin, T end, V stepsize) -> RangeImpl<decltype(begin + end + stepsize)>
    {
        /// @note 类型由 begin/end/stepsize 三者推断
        return RangeImpl<decltype(begin + end + stepsize)>(begin, end, stepsize);
    }

    template <typename T>
    RangeImpl<T> Range(T begin, T end) ///< 两个参数
    {
        return RangeImpl<T>(begin, end, 1); ///< 默认 stepsize = 1
    }

    template <typename T>
    RangeImpl<T> Range(T end) ///< 一个参数
    {
        return RangeImpl<T>(T(), end, 1); ///< 默认 stepsize = 1
    }
}

测试代码如下:

void TestRange()
{
    cout << "Range(15):";         ///< 默认从 0 开始的整数
    for (int i : Range(15))
    {
        cout << " " << i;
    }
    cout << endl;
    cout << "Range(2,6):";        ///< begin/end 为整数
    for (int i : Range(2, 6))
    {
        cout << " " << i;
    }
    cout << endl;
    cout << "Range(10.5, 15.5):"; ///< begin/end 为浮点数
    for (float i : Range(10.5, 15.5))
    {
        cout << " " << i;
    }
    cout << endl;
    cout << "Range(35, 27, -1):"; ///< stepsize 为负整数【反向迭代】
    for (int i : Range(35, 27, -1)) 
    {
        cout << " " << i;
    }
    cout << endl;
    cout << "Range(2, 8, 0.5):"; ///< stepsize 为浮点数
    for (float i : Range(2, 8, 0.5))
    {
        cout << " " << i;
    }
    cout << endl;
    cout << "Range(8, 7, -0.1):"; ///< stepsize 为负浮点数【反向迭代】
    for (auto i : Range(8, 7, -0.1))
    {
        cout << " " << i;
    }
    cout << endl;

    cout << "Range('a', 'z'):";   ///< 遍历字符
    for (auto i : Range('a', 'z'))
    {
        cout << " " << i;
    }
    cout << endl;
}

输出结果为

image.png