- Mastering the C++17 STL
- Arthur O'Dwyer
- 704字
- 2021-07-08 10:20:22
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.
- JavaScript高效圖形編程
- Docker進階與實戰
- MySQL 8 DBA基礎教程
- 差分進化算法及其高維多目標優化應用
- Getting Started with Greenplum for Big Data Analytics
- Python3.5從零開始學
- Visual Basic程序設計基礎
- C語言程序設計與應用實驗指導書(第2版)
- JavaWeb從入門到精通(視頻實戰版)
- Qt編程快速入門
- Learning SaltStack(Second Edition)
- Python High Performance(Second Edition)
- Python游戲編程項目開發實戰
- 計算機應用基礎
- Mastering Web Application Development with Express