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

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.

主站蜘蛛池模板: 柏乡县| 滨海县| 和林格尔县| 兴山县| 宕昌县| 准格尔旗| 涞源县| 西华县| 东乡县| 普格县| 屏东市| 祁阳县| 独山县| 昆明市| 开化县| 大竹县| 荥经县| 石林| 英吉沙县| 峡江县| 拉孜县| 阿图什市| 镇康县| 吉首市| 深圳市| 达拉特旗| 克拉玛依市| 来凤县| 阳山县| 原阳县| 宿松县| 揭阳市| 万州区| 黄大仙区| 息烽县| 沙坪坝区| 沾益县| 翼城县| 米泉市| 绥芬河市| 辽中县|