开头
之前写了部分关于c++11让程序更简洁相关内容,至于分出来第二部分,是由于之前都展示的一些纯语法相关并未主动编写代码,这里有一个需要编写一些代码的,所以单独摘出来。
让基于范围的for循环(支持自定义类型)
如何让基于范围的for循环运转
具体来说,基于范围的for循环只是普通for循环的语法糖,它需要查找容器的begin和end迭代器。
1. 对于普通array对象,begin将为array首地址,end为首地址加容器长度;
2. 容器是类对象,range-based for将试图通过查找类的begin()和end()定位;
3. 其他,通过全局begin和end定位。
实现对区间的迭代
namespace detail_range {
template<typename T>
class iterator
{
public:
using value_type = T;
using size_type = size_t;
private:
size_type cursor_;
const value_type step_;
value_type value_;
public:
iterator(size_type cur_start, value_type begin_val, value_type step_val)
: cursor_(cur_start), step_(step_val), value_(begin_val)
{
value_ += (step_ * cursor_);
}
value_type operator*() const { return value_; }
bool operator!=(const iterator& rhs) const
{
return cursor_ != rhs.cursor_;
}
iterator& operator++()
{
value_ += step_;
++cursor_;
return *this;
}
};
template<typename T>
class impl
{
public:
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = detail_range::iterator<value_type>;
using const_iterator = const detail_range::iterator<value_type>;
using size_type = typename iterator::size_type;
private:
const value_type begin_;
const value_type end_;
const value_type step_;
const size_type max_count_;
size_type get_adjusted_count() const
{
if (step_ > 0 && begin_ >= end_) {
throw std::logic_error("when step > 0, end value must be greater than begin value");
} else if (step_ < 0 && begin_ <= end_) {
throw std::logic_error("when step < 0, end value must be smaller than begin value");
}
size_type count = static_cast<size_type>((end_ - begin_) / step_);
if (begin_ + (step_ * count) != end_) {
++count;
}
return count;
}
public:
impl(value_type begin_val, value_type end_val, value_type step_val)
: begin_(begin_val)
, end_(end_val)
, step_(step_val)
, max_count_(get_adjusted_count())
{}
size_type size() const
{
return max_count_;
}
const_iterator begin() const {
return{0, begin_, step_ };
}
const_iterator end() const {
return{ max_count_, begin_, step_ };
}
};
template<typename T>
impl<T> range(T end) {
return{ {0}, end, 1 };
}
template<typename T>
impl<T> range(T begin, T end) {
return{ begin, end, 1 };
}
template<typename T, typename U>
auto range(T begin, T end, U step)->impl<decltype(begin + step)> {
using r_t = impl<decltype(begin + step)>;
return r_t( begin, end, step );
}
}
使用:
int main()
{
for (auto i : detail_range::range(5, 20, 0.5)) {
cout << i << endl;
}
}
以上代码主要摘抄书中的例子,有几点觉得书中写得很好:
- 实现impl中将类型进行了统一,避免了impl中的一些不必要的复杂度,然后在rang中将step和begin类型进行统一;
- using r_t = impl
尝试自己改造一个rang,支持从标准容器中间隔取值
附上代码吧,还缺少一些出错判断但能满足一般需求,待真实使用中进一步改进:
#pragma once
#include <assert.h>
namespace container_range {
template<typename T>
class iterator
{
public:
using iterator_type = T;
using size_type = size_t;
using value_type = typename iterator_type::value_type;
private:
iterator_type cursor_;
iterator_type end_;
const int step_;
public:
iterator(iterator_type cur_start, iterator_type cur_end, int step_val)
: cursor_(cur_start), end_(cur_end), step_(step_val)
{
assert(step_ >= 0);
}
value_type operator*() const { return *cursor_; }
bool operator!=(const iterator& rhs) const
{
return cursor_ != rhs.cursor_;
}
iterator& operator++()
{
auto diff = std::distance(cursor_, end_);
assert(diff >= 0);
if (step_ < diff) {
std::advance(cursor_, step_);
}
else {
cursor_ = end_;
}
return *this;
}
};
template<typename ContainerT>
class impl{
public:
using container_type = ContainerT;
using value_type = typename ContainerT::value_type;
using const_reference = typename ContainerT::const_reference;
using container_iterator_type = decltype(ContainerT().begin());
private:
ContainerT container_;
int step_;
int begin_;
public:
impl(ContainerT container, int begin, int step) : container_(container), begin_(begin), step_(step) {}
iterator<container_iterator_type> begin() {
auto beginIt = container_.begin();
std::advance(beginIt, begin_);
return{ beginIt, container_.end(), step_};
}
iterator<container_iterator_type> end() {
return{ container_.end(), container_.end(), step_};
}
};
template<typename ContainerT>
impl<ContainerT> range(ContainerT container, int begin, int step) {
return{container, begin, step};
}
}
下面是使用例子:
#include <iostream>
#include "containerRange.h"
#include <list>
int main()
{
std::list<int> l = {1, 2, 3, 4, 5, 6, 7};
for (auto i : container_range::range(l, 0, 2)) { // 从第一个1个元素开始取,间隔为2
std::cout << i << std::endl;
}
int v = 0;
std::cin >> v;
return 0;
}
输出结果如下,满足初始设计要求。
1
3
5
7