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

The deprecated std::iterator

You might be wondering: "Every iterator class I implement needs to provide the same five member typedefs. That's a lot of boilerplate--a lot of typing that I'd like to factor out, if I could." Is there no way to eliminate all that boilerplate?

Well, in C++98, and up until C++17, the standard library included a helper class template to do exactly that. Its name was std::iterator, and it took five template type parameters that corresponded to the five member typedefs required by std::iterator_traits. Three of these parameters had "sensible defaults," meaning that the simplest use-case was pretty well covered:

    namespace std {
template<
class Category,
class T,
class Distance = std::ptrdiff_t,
class Pointer = T*,
class Reference = T&
> struct iterator {
using iterator_category = Category;
using value_type = T;
using difference_type = Distance;
using pointer = Pointer;
using reference = Reference;
};
}

class list_of_ints_iterator :
public std::iterator<std::forward_iterator_tag, int>
{
// ...
};

Unfortunately for std::iterator, real life wasn't that simple; and std::iterator was deprecated in C++17 for several reasons that we're about to discuss.

As we saw in the section Const iterators, const-correctness requires us to provide a const iterator type along with every "non-const iterator" type. So what we really end up with, following that example, is code like this:

    template<
bool Const,
class Base = std::iterator<
std::forward_iterator_tag,
int,
std::ptrdiff_t,
std::conditional_t<Const, const int*, int*>,
std::conditional_t<Const, const int&, int&>
>
>
class list_of_ints_iterator : public Base
{
using typename Base::reference; // Awkward!

using node_pointer = std::conditional_t<Const, const list_node*,
list_node*>;
node_pointer ptr_;

public:
reference operator*() const { return ptr_->data; }
// ...
};

The preceding code isn't any easier to read or write than the version that didn't use std::iterator; and furthermore, using std::iterator in the intended fashion complicates our code with public inheritance, which is to say, something that looks an awful lot like the classical object-oriented class hierarchy. A beginner might well be tempted to use that class hierarchy in writing functions like this one:

    template<typename... Ts, typename Predicate>
int count_if(const std::iterator<Ts...>& begin,
const std::iterator<Ts...>& end,
Predicate pred);

This looks superficially similar to our examples of "polymorphic programming" from Chapter 1, Classical Polymorphism and Generic Programming, a function that implements different behaviors by taking parameters of type reference-to-base-class. But in the case of std::iterator this similarity is purely accidental and misleading; inheriting from std::iterator does not give us a polymorphic class hierarchy, and referring to that "base class" from our own functions is never the correct thing to do!

So, the C++17 standard deprecates std::iterator with an eye toward removing it completely in 2020 or some later standard. You shouldn't use std::iterator in code you write.

However, if you use Boost in your codebase, you might want to check out the Boost equivalent of std::iterator, which is spelled boost::iterator_facade. Unlike std::iterator, the boost::iterator_facade base class provides default functionality for pesky member functions such as operator++(int) and operator!= that would otherwise be tedious boilerplate. To use iterator_facade, simply inherit from it and define a few primitive member functions such as dereference, increment, and equal. (Since our list iterator is a ForwardIterator, that's all we need. For a BidirectionalIterator you would also need to provide a decrement member function, and so on.)

Since these primitive member functions are private, we grant Boost access to them via the declaration friend class boost::iterator_core_access;:

    #include <boost/iterator/iterator_facade.hpp>

template<bool Const>
class list_of_ints_iterator : public boost::iterator_facade<
list_of_ints_iterator<Const>,
std::conditional_t<Const, const int, int>,
std::forward_iterator_tag
>
{
friend class boost::iterator_core_access;
friend class list_of_ints;
friend class list_of_ints_iterator<!Const>;

using node_pointer = std::conditional_t<Const, const list_node*,
list_node*>;
node_pointer ptr_;

explicit list_of_ints_iterator(node_pointer p) : ptr_(p) {}

auto& dereference() const { return ptr_->data; }
void increment() { ptr_ = ptr_->next; }

// Support comparison between iterator and const_iterator types
template<bool R>
bool equal(const list_of_ints_iterator<R>& rhs) const {
return ptr_ == rhs.ptr_;}

public:
// Support implicit conversion of iterator to const_iterator
// (but not vice versa)
operator list_of_ints_iterator<true>() const { return
list_of_ints_iterator<true>{ptr_}; }
};

Notice that the first template type argument to boost::iterator_facade is always the class whose definition you're writing: this is the Curiously Recurring Template Pattern, which we'll see again in Chapter 6, Smart Pointers.

This list-iterator code using boost::iterator_facade is significantly shorter than the same code in the previous section; the savings comes mainly from not having to repeat the relational operators. Because our list iterator is a ForwardIterator, we only had two relational operators; but if it were a RandomAccessIterator, then iterator_facade would generate default implementations of operators -, <, >, <=, and >= all based on the single primitive member function distance_to.

主站蜘蛛池模板: 南涧| 府谷县| 敦煌市| 丰镇市| 韶关市| 东光县| 东海县| 丰都县| 肃南| 阳曲县| 马边| 吉水县| 桐梓县| 琼中| 新余市| 金川县| 栾城县| 通州市| 康定县| 鄂托克前旗| 海林市| 南召县| 清镇市| 彝良县| 华亭县| 绥江县| 衡阳县| 莆田市| 宁国市| 廉江市| 虎林市| 博野县| 淳化县| 河源市| 安康市| 南丹县| 万年县| 吉林市| 册亨县| 阳曲县| 平远县|