官术网_书友最值得收藏!

  • Mastering the C++17 STL
  • Arthur O'Dwyer
  • 696字
  • 2021-07-08 10:20:21

Iterator categories

Let's revisit the count and count_if functions that we introduced in
Chapter 1, Classical Polymorphism and Generic Programming. Compare the function template definition in this next example to the similar code from that chapter; you'll see that it's identical except for the substitution of a pair of Iterators (that is, an implicitly defined range) for the Container& parameter--and except that I've changed the name of the first function from count to distance. That's because you can find this function, almost exactly as described here, in the Standard Template Library under the name std::distance and you can find the second function under the name std::count_if:

    template<typename Iterator>
int distance(Iterator begin, Iterator end)
{
int sum = 0;
for (auto it = begin; it != end; ++it) {
sum += 1;
}
return sum;
}

template<typename Iterator, typename Predicate>
int count_if(Iterator begin, Iterator end, Predicate pred)
{
int sum = 0;
for (auto it = begin; it != end; ++it) {
if (pred(*it)) {
sum += 1;
}
}
return sum;
}

void test()
{
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};

int number_above = count_if(v.begin(), v.end(), [](int e) { return e > 5; });
int number_below = count_if(v.begin(), v.end(), [](int e) { return e < 5; });

int total = distance(v.begin(), v.end()); // DUBIOUS

assert(number_above == 2);
assert(number_below == 5);
assert(total == 8);
}

But let's consider the line marked DUBIOUS in that example. Here we're computing the distance between two Iterators by repeatedly incrementing the one until it reaches the other. How performant is this approach? For certain kinds of iterators--for example, list_of_ints::iterator--we're not going to be able to do better than this. But for vector::iterator or int*, which iterate over contiguous data, it's a little silly of us to be using a loop and an O(n) algorithm when we could accomplish the same thing in O(1) time by simple pointer subtraction. That is, we'd like the standard library version of std::distance to include a template specialization something like this:

    template<typename Iterator>
int distance(Iterator begin, Iterator end)
{
int sum = 0;
for (auto it = begin; it != end; ++it) {
sum += 1;
}
return sum;
}

template<>
int distance(int *begin, int *end)
{
return end - begin;
}

But we don't want the specialization to exist only for int* and std::vector::iterator. We want the standard library's std::distance to be efficient for all the iterator types that support this particular operation. That is, we're starting to develop an intuition that there are (at least) two different kinds of iterators: there are those that are incrementable, comparable, and dereferenceable; and then there are those that are incrementable, comparable, dereferenceable, and also subtractable! It turns out that for any iterator type where the operation i = p - q makes sense, its inverse operation q = p + i also makes sense. Iterators that support subtraction and addition are called random-access iterators.

So, the standard library's std::distance ought to be efficient for both random-access iterators and other kinds of iterators. To make it easier to supply the partial specializations for these templates, the standard library introduced the idea of a hierarchy of iterator kinds. Iterators such as int*, which support addition and subtraction, are known as random-access iterators. We'll say that they satisfy the concept RandomAccessIterator.

Iterators slightly less powerful than random-access iterators might not support addition or subtraction of arbitrary distances, but they at least support incrementing and decrementing with ++p and --p. Iterators of this nature are called BidirectionalIterator. All RandomAccessIterator are BidirectionalIterator, but not necessarily vice versa. In some sense, we can imagine RandomAccessIterator to be a sub-class or sub-concept relative to BidirectionalIterator; and we can say that BidirectionalIterator is a weaker concept, imposing fewer requirements, than RandomAccessIterator.

An even weaker concept is the kind of iterators that don't even support decrementing. For example, our list_of_ints::iterator type doesn't support decrementing, because our linked list has no previous pointers; once you've got an iterator pointing at a given element of the list, you can only move forward to later elements, never backward to previous ones. Iterators that support ++p but not --p are called ForwardIterator. ForwardIterator is a weaker concept than BidirectionalIterator.

主站蜘蛛池模板: 井冈山市| 姚安县| 白银市| 申扎县| 和田市| 礼泉县| 梁平县| 清徐县| 家居| 涿鹿县| 循化| 浪卡子县| 大悟县| 即墨市| 临漳县| 马尔康县| 安丘市| 紫云| 新宁县| 金湖县| 潼关县| 景谷| 河东区| 湘阴县| 曲靖市| 达尔| 弋阳县| 南昌县| 通许县| 临高县| 焉耆| 洮南市| 汝州市| 吉水县| 罗山县| 济源市| 兴安县| 肇源县| 进贤县| 旅游| 益阳市|